summaryrefslogtreecommitdiffstats
path: root/usr.bin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/Makefile68
-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.c224
-rw-r--r--usr.bin/ar/Makefile10
-rw-r--r--usr.bin/ar/append.c89
-rw-r--r--usr.bin/ar/ar.1318
-rw-r--r--usr.bin/ar/ar.1aout318
-rw-r--r--usr.bin/ar/ar.5146
-rw-r--r--usr.bin/ar/ar.5.5146
-rw-r--r--usr.bin/ar/ar.c243
-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/Makefile33
-rw-r--r--usr.bin/at/Makefile.inc19
-rw-r--r--usr.bin/at/at.c773
-rw-r--r--usr.bin/at/at.h31
-rw-r--r--usr.bin/at/at.man268
-rw-r--r--usr.bin/at/panic.c80
-rw-r--r--usr.bin/at/panic.h44
-rw-r--r--usr.bin/at/parsetime.c621
-rw-r--r--usr.bin/at/parsetime.h26
-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.668
-rw-r--r--usr.bin/banner/banner.c1160
-rw-r--r--usr.bin/basename/Makefile6
-rw-r--r--usr.bin/basename/basename.198
-rw-r--r--usr.bin/basename/basename.c139
-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.189
-rw-r--r--usr.bin/biff/biff.c106
-rw-r--r--usr.bin/brandelf/Makefile3
-rw-r--r--usr.bin/brandelf/brandelf.184
-rw-r--r--usr.bin/brandelf/brandelf.c120
-rw-r--r--usr.bin/cal/Makefile5
-rw-r--r--usr.bin/cal/README42
-rw-r--r--usr.bin/cal/cal.182
-rw-r--r--usr.bin/cal/cal.c441
-rw-r--r--usr.bin/calendar/Makefile18
-rw-r--r--usr.bin/calendar/calendar.1230
-rw-r--r--usr.bin/calendar/calendar.c140
-rw-r--r--usr.bin/calendar/calendar.h73
-rw-r--r--usr.bin/calendar/calendars/calendar.all16
-rw-r--r--usr.bin/calendar/calendars/calendar.birthday273
-rw-r--r--usr.bin/calendar/calendars/calendar.christian28
-rw-r--r--usr.bin/calendar/calendars/calendar.computer73
-rw-r--r--usr.bin/calendar/calendars/calendar.croatian12
-rw-r--r--usr.bin/calendar/calendars/calendar.german12
-rw-r--r--usr.bin/calendar/calendars/calendar.history499
-rw-r--r--usr.bin/calendar/calendars/calendar.holiday579
-rw-r--r--usr.bin/calendar/calendars/calendar.judaic41
-rw-r--r--usr.bin/calendar/calendars/calendar.music190
-rw-r--r--usr.bin/calendar/calendars/calendar.russian12
-rw-r--r--usr.bin/calendar/calendars/calendar.usholiday42
-rw-r--r--usr.bin/calendar/calendars/calendar.world18
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.all17
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.feiertag42
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.geschichte197
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.kirche32
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.literatur36
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.musik25
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.wissenschaft19
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.all17
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.feiertag42
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.geschichte197
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.kirche32
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.literatur36
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.musik25
-rw-r--r--usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.wissenschaft19
-rw-r--r--usr.bin/calendar/calendars/hr_HR.ISO8859-2/calendar.all12
-rw-r--r--usr.bin/calendar/calendars/hr_HR.ISO8859-2/calendar.praznici37
-rw-r--r--usr.bin/calendar/calendars/hr_HR.ISO_8859-2/calendar.all12
-rw-r--r--usr.bin/calendar/calendars/hr_HR.ISO_8859-2/calendar.praznici37
-rw-r--r--usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.all15
-rw-r--r--usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.common24
-rw-r--r--usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.msk16
-rw-r--r--usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.orthodox34
-rw-r--r--usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.pagan42
-rw-r--r--usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.all15
-rw-r--r--usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.common24
-rw-r--r--usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.msk16
-rw-r--r--usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.orthodox34
-rw-r--r--usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.pagan42
-rw-r--r--usr.bin/calendar/day.c487
-rw-r--r--usr.bin/calendar/io.c356
-rw-r--r--usr.bin/calendar/ostern.c124
-rw-r--r--usr.bin/calendar/paskha.c93
-rw-r--r--usr.bin/calendar/pathnames.h41
-rw-r--r--usr.bin/cap_mkdb/Makefile5
-rw-r--r--usr.bin/cap_mkdb/cap_mkdb.1102
-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.8315
-rw-r--r--usr.bin/chat/chat.c1348
-rwxr-xr-xusr.bin/chat/connect-ppp129
-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.c616
-rw-r--r--usr.bin/chflags/Makefile9
-rw-r--r--usr.bin/chflags/chflags.1137
-rw-r--r--usr.bin/chflags/chflags.c180
-rw-r--r--usr.bin/chkey/Makefile14
-rw-r--r--usr.bin/chkey/chkey.121
-rw-r--r--usr.bin/chkey/chkey.c288
-rw-r--r--usr.bin/chpass/Makefile63
-rw-r--r--usr.bin/chpass/chpass.1422
-rw-r--r--usr.bin/chpass/chpass.c291
-rw-r--r--usr.bin/chpass/chpass.h70
-rw-r--r--usr.bin/chpass/edit.c254
-rw-r--r--usr.bin/chpass/field.c287
-rw-r--r--usr.bin/chpass/pathnames.h39
-rw-r--r--usr.bin/chpass/pw_copy.c132
-rw-r--r--usr.bin/chpass/pw_copy.h36
-rw-r--r--usr.bin/chpass/pw_yp.c521
-rw-r--r--usr.bin/chpass/pw_yp.h126
-rw-r--r--usr.bin/chpass/table.c64
-rw-r--r--usr.bin/chpass/util.c146
-rw-r--r--usr.bin/cksum/Makefile8
-rw-r--r--usr.bin/cksum/cksum.1180
-rw-r--r--usr.bin/cksum/cksum.c138
-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.1115
-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.c111
-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.1127
-rw-r--r--usr.bin/col/col.c551
-rw-r--r--usr.bin/colcrt/Makefile5
-rw-r--r--usr.bin/colcrt/colcrt.1108
-rw-r--r--usr.bin/colcrt/colcrt.c257
-rw-r--r--usr.bin/colldef/Makefile13
-rw-r--r--usr.bin/colldef/colldef.1252
-rw-r--r--usr.bin/colldef/data/Makefile72
-rw-r--r--usr.bin/colldef/data/de_DE.ISO_8859-1.src38
-rw-r--r--usr.bin/colldef/data/es_ES.ISO_8859-1.src38
-rw-r--r--usr.bin/colldef/data/is_IS.ISO_8859-1.src38
-rw-r--r--usr.bin/colldef/data/lt_LN.ASCII.src6
-rw-r--r--usr.bin/colldef/data/lt_LN.ISO_8859-1.src38
-rw-r--r--usr.bin/colldef/data/lt_LN.ISO_8859-2.src36
-rw-r--r--usr.bin/colldef/data/map.CP866174
-rw-r--r--usr.bin/colldef/data/map.ISO_8859-1174
-rw-r--r--usr.bin/colldef/data/map.ISO_8859-2174
-rw-r--r--usr.bin/colldef/data/map.KOI8-R174
-rw-r--r--usr.bin/colldef/data/ru_SU.CP866.src39
-rw-r--r--usr.bin/colldef/data/ru_SU.KOI8-R.src39
-rw-r--r--usr.bin/colldef/en_DK.example3073
-rw-r--r--usr.bin/colldef/parse.y289
-rw-r--r--usr.bin/colldef/scan.l293
-rw-r--r--usr.bin/colrm/Makefile5
-rw-r--r--usr.bin/colrm/colrm.178
-rw-r--r--usr.bin/colrm/colrm.c141
-rw-r--r--usr.bin/column/Makefile5
-rw-r--r--usr.bin/column/column.199
-rw-r--r--usr.bin/column/column.c305
-rw-r--r--usr.bin/comm/Makefile5
-rw-r--r--usr.bin/comm/comm.195
-rw-r--r--usr.bin/comm/comm.c185
-rw-r--r--usr.bin/compile_et/Makefile17
-rw-r--r--usr.bin/compile_et/compile_et.178
-rw-r--r--usr.bin/compile_et/compile_et.c276
-rw-r--r--usr.bin/compile_et/compiler.h19
-rw-r--r--usr.bin/compile_et/error_table.y237
-rw-r--r--usr.bin/compile_et/et_lex.lex.l26
-rw-r--r--usr.bin/compile_et/mit-sipb-copyright.h19
-rw-r--r--usr.bin/compress/Makefile11
-rw-r--r--usr.bin/compress/compress.1172
-rw-r--r--usr.bin/compress/compress.c446
-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/zopen.3140
-rw-r--r--usr.bin/compress/zopen.c741
-rw-r--r--usr.bin/compress/zopen.h34
-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.c277
-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.c270
-rw-r--r--usr.bin/dig/Makefile12
-rw-r--r--usr.bin/dirname/Makefile6
-rw-r--r--usr.bin/dirname/dirname.c145
-rw-r--r--usr.bin/dnsquery/Makefile10
-rw-r--r--usr.bin/du/Makefile5
-rw-r--r--usr.bin/du/du.1135
-rw-r--r--usr.bin/du/du.c247
-rw-r--r--usr.bin/ee/Artistic117
-rw-r--r--usr.bin/ee/Makefile27
-rw-r--r--usr.bin/ee/README116
-rw-r--r--usr.bin/ee/ee.1529
-rw-r--r--usr.bin/ee/ee.c5128
-rw-r--r--usr.bin/ee/ee.i18n.guide141
-rw-r--r--usr.bin/ee/ee.msg179
-rw-r--r--usr.bin/ee/new_curse.c3574
-rw-r--r--usr.bin/ee/new_curse.h255
-rw-r--r--usr.bin/ee/nls/de_DE.ISO8859-1/ee.msg182
-rw-r--r--usr.bin/ee/nls/de_DE.ISO_8859-1/ee.msg182
-rw-r--r--usr.bin/ee/nls/en_US.ISO_8859-1/ee.msg182
-rw-r--r--usr.bin/ee/nls/en_US.US-ASCII/ee.msg182
-rw-r--r--usr.bin/ee/nls/fr_FR.ISO8859-1/ee.msg182
-rw-r--r--usr.bin/ee/nls/fr_FR.ISO_8859-1/ee.msg182
-rw-r--r--usr.bin/env/Makefile6
-rw-r--r--usr.bin/env/env.c90
-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.c774
-rw-r--r--usr.bin/expand/Makefile6
-rw-r--r--usr.bin/expand/expand.187
-rw-r--r--usr.bin/expand/expand.c184
-rw-r--r--usr.bin/f2c/Makefile35
-rw-r--r--usr.bin/f2c/Notice23
-rw-r--r--usr.bin/f2c/README168
-rw-r--r--usr.bin/f2c/cds.c195
-rw-r--r--usr.bin/f2c/data.c493
-rw-r--r--usr.bin/f2c/defines.h300
-rw-r--r--usr.bin/f2c/defs.h1055
-rw-r--r--usr.bin/f2c/equiv.c413
-rw-r--r--usr.bin/f2c/error.c347
-rw-r--r--usr.bin/f2c/exec.c934
-rw-r--r--usr.bin/f2c/expr.c3436
-rw-r--r--usr.bin/f2c/f2c.1301
-rw-r--r--usr.bin/f2c/f2c.h223
-rw-r--r--usr.bin/f2c/format.c2524
-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.dcl416
-rw-r--r--usr.bin/f2c/gram.exec143
-rw-r--r--usr.bin/f2c/gram.expr142
-rw-r--r--usr.bin/f2c/gram.head291
-rw-r--r--usr.bin/f2c/gram.io173
-rw-r--r--usr.bin/f2c/init.c517
-rw-r--r--usr.bin/f2c/intr.c977
-rw-r--r--usr.bin/f2c/io.c1508
-rw-r--r--usr.bin/f2c/iob.h26
-rw-r--r--usr.bin/f2c/lex.c1707
-rw-r--r--usr.bin/f2c/machdefs.h31
-rw-r--r--usr.bin/f2c/main.c708
-rw-r--r--usr.bin/f2c/malloc.c165
-rw-r--r--usr.bin/f2c/mem.c268
-rw-r--r--usr.bin/f2c/memset.c66
-rw-r--r--usr.bin/f2c/misc.c1329
-rw-r--r--usr.bin/f2c/names.c835
-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/output.c1709
-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.c557
-rw-r--r--usr.bin/f2c/pccdefs.h64
-rw-r--r--usr.bin/f2c/pread.c990
-rw-r--r--usr.bin/f2c/proc.c1821
-rw-r--r--usr.bin/f2c/put.c441
-rw-r--r--usr.bin/f2c/putpcc.c2075
-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.c570
-rw-r--r--usr.bin/f2c/version.c2
-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/fetch/Makefile9
-rw-r--r--usr.bin/fetch/fetch.1303
-rw-r--r--usr.bin/fetch/fetch.h89
-rw-r--r--usr.bin/fetch/file.c144
-rw-r--r--usr.bin/fetch/ftp.c427
-rw-r--r--usr.bin/fetch/http.c1605
-rw-r--r--usr.bin/fetch/main.c353
-rw-r--r--usr.bin/fetch/uri.c122
-rw-r--r--usr.bin/fetch/util.c332
-rw-r--r--usr.bin/file/LEGAL.NOTICE31
-rw-r--r--usr.bin/file/MAINT33
-rw-r--r--usr.bin/file/Magdir/Header5
-rw-r--r--usr.bin/file/Magdir/Localstuff7
-rw-r--r--usr.bin/file/Magdir/alliant17
-rw-r--r--usr.bin/file/Magdir/alpha21
-rw-r--r--usr.bin/file/Magdir/amanda7
-rw-r--r--usr.bin/file/Magdir/amigaos10
-rw-r--r--usr.bin/file/Magdir/animation58
-rw-r--r--usr.bin/file/Magdir/apl6
-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/archive210
-rw-r--r--usr.bin/file/Magdir/asterix17
-rw-r--r--usr.bin/file/Magdir/att3b39
-rw-r--r--usr.bin/file/Magdir/audio96
-rw-r--r--usr.bin/file/Magdir/blit19
-rw-r--r--usr.bin/file/Magdir/bsdi7
-rw-r--r--usr.bin/file/Magdir/bzip14
-rw-r--r--usr.bin/file/Magdir/c-lang13
-rw-r--r--usr.bin/file/Magdir/chi7
-rw-r--r--usr.bin/file/Magdir/clipper64
-rw-r--r--usr.bin/file/Magdir/commands75
-rw-r--r--usr.bin/file/Magdir/compress92
-rw-r--r--usr.bin/file/Magdir/convex69
-rw-r--r--usr.bin/file/Magdir/cpio16
-rw-r--r--usr.bin/file/Magdir/database38
-rw-r--r--usr.bin/file/Magdir/diamond11
-rw-r--r--usr.bin/file/Magdir/diff8
-rw-r--r--usr.bin/file/Magdir/digital41
-rw-r--r--usr.bin/file/Magdir/ditroff4
-rw-r--r--usr.bin/file/Magdir/dump81
-rw-r--r--usr.bin/file/Magdir/elf76
-rw-r--r--usr.bin/file/Magdir/encore21
-rw-r--r--usr.bin/file/Magdir/figlet9
-rw-r--r--usr.bin/file/Magdir/filesystems6
-rw-r--r--usr.bin/file/Magdir/floppy.raw1
-rw-r--r--usr.bin/file/Magdir/fonts26
-rw-r--r--usr.bin/file/Magdir/frame37
-rw-r--r--usr.bin/file/Magdir/freebsd130
-rw-r--r--usr.bin/file/Magdir/gzip21
-rw-r--r--usr.bin/file/Magdir/hp228
-rw-r--r--usr.bin/file/Magdir/ibm37047
-rw-r--r--usr.bin/file/Magdir/ibm600017
-rw-r--r--usr.bin/file/Magdir/iff28
-rw-r--r--usr.bin/file/Magdir/imagen14
-rw-r--r--usr.bin/file/Magdir/images236
-rw-r--r--usr.bin/file/Magdir/intel35
-rw-r--r--usr.bin/file/Magdir/interleaf8
-rw-r--r--usr.bin/file/Magdir/iris57
-rw-r--r--usr.bin/file/Magdir/island9
-rw-r--r--usr.bin/file/Magdir/ispell54
-rw-r--r--usr.bin/file/Magdir/java13
-rw-r--r--usr.bin/file/Magdir/karma8
-rw-r--r--usr.bin/file/Magdir/lex11
-rw-r--r--usr.bin/file/Magdir/lif7
-rw-r--r--usr.bin/file/Magdir/linux73
-rw-r--r--usr.bin/file/Magdir/lisp10
-rw-r--r--usr.bin/file/Magdir/mach38
-rw-r--r--usr.bin/file/Magdir/magic5
-rw-r--r--usr.bin/file/Magdir/mail.news21
-rw-r--r--usr.bin/file/Magdir/microsoft72
-rw-r--r--usr.bin/file/Magdir/mips8
-rw-r--r--usr.bin/file/Magdir/mirage7
-rw-r--r--usr.bin/file/Magdir/mkid10
-rw-r--r--usr.bin/file/Magdir/mmdf5
-rw-r--r--usr.bin/file/Magdir/motorola32
-rw-r--r--usr.bin/file/Magdir/ms-dos73
-rw-r--r--usr.bin/file/Magdir/ncr48
-rw-r--r--usr.bin/file/Magdir/netbsd209
-rw-r--r--usr.bin/file/Magdir/news12
-rw-r--r--usr.bin/file/Magdir/osf110
-rw-r--r--usr.bin/file/Magdir/pbm7
-rw-r--r--usr.bin/file/Magdir/pdf7
-rw-r--r--usr.bin/file/Magdir/pdp25
-rw-r--r--usr.bin/file/Magdir/pgp13
-rw-r--r--usr.bin/file/Magdir/pjl5
-rw-r--r--usr.bin/file/Magdir/pkgadd5
-rw-r--r--usr.bin/file/Magdir/plus517
-rw-r--r--usr.bin/file/Magdir/postscript20
-rw-r--r--usr.bin/file/Magdir/printer55
-rw-r--r--usr.bin/file/Magdir/psdbms7
-rw-r--r--usr.bin/file/Magdir/pyramid11
-rw-r--r--usr.bin/file/Magdir/rle19
-rw-r--r--usr.bin/file/Magdir/rpm17
-rw-r--r--usr.bin/file/Magdir/rtf12
-rw-r--r--usr.bin/file/Magdir/sc5
-rw-r--r--usr.bin/file/Magdir/sccs21
-rw-r--r--usr.bin/file/Magdir/sendmail10
-rw-r--r--usr.bin/file/Magdir/sequent34
-rw-r--r--usr.bin/file/Magdir/sgi170
-rw-r--r--usr.bin/file/Magdir/sgml21
-rw-r--r--usr.bin/file/Magdir/sniffer63
-rw-r--r--usr.bin/file/Magdir/softquad30
-rw-r--r--usr.bin/file/Magdir/sun110
-rw-r--r--usr.bin/file/Magdir/sunraster12
-rw-r--r--usr.bin/file/Magdir/terminfo9
-rw-r--r--usr.bin/file/Magdir/tex36
-rw-r--r--usr.bin/file/Magdir/timezone12
-rw-r--r--usr.bin/file/Magdir/troff27
-rw-r--r--usr.bin/file/Magdir/typeset7
-rw-r--r--usr.bin/file/Magdir/unknown36
-rw-r--r--usr.bin/file/Magdir/uuencode30
-rw-r--r--usr.bin/file/Magdir/varied.out18
-rw-r--r--usr.bin/file/Magdir/vax34
-rw-r--r--usr.bin/file/Magdir/visx31
-rw-r--r--usr.bin/file/Magdir/vms27
-rw-r--r--usr.bin/file/Magdir/x1111
-rw-r--r--usr.bin/file/Magdir/xenix72
-rw-r--r--usr.bin/file/Magdir/zilog11
-rw-r--r--usr.bin/file/Magdir/zyxel16
-rw-r--r--usr.bin/file/Makefile56
-rw-r--r--usr.bin/file/PORTING76
-rw-r--r--usr.bin/file/README91
-rw-r--r--usr.bin/file/apprentice.c621
-rw-r--r--usr.bin/file/ascmagic.c124
-rw-r--r--usr.bin/file/compress.c122
-rw-r--r--usr.bin/file/cvsimport.sh18
-rw-r--r--usr.bin/file/file.1366
-rw-r--r--usr.bin/file/file.c398
-rw-r--r--usr.bin/file/file.h151
-rw-r--r--usr.bin/file/fsmagic.c181
-rw-r--r--usr.bin/file/internat.c72
-rw-r--r--usr.bin/file/is_tar.c100
-rw-r--r--usr.bin/file/magic.5206
-rw-r--r--usr.bin/file/names.h99
-rw-r--r--usr.bin/file/patchlevel.h146
-rw-r--r--usr.bin/file/print.c204
-rw-r--r--usr.bin/file/readelf.c318
-rw-r--r--usr.bin/file/readelf.h167
-rw-r--r--usr.bin/file/softmagic.c513
-rw-r--r--usr.bin/file/tar.h179
-rw-r--r--usr.bin/file2c/Makefile6
-rw-r--r--usr.bin/file2c/file2c.146
-rw-r--r--usr.bin/file2c/file2c.c46
-rw-r--r--usr.bin/find/Makefile7
-rw-r--r--usr.bin/find/extern.h80
-rw-r--r--usr.bin/find/find.1489
-rw-r--r--usr.bin/find/find.c202
-rw-r--r--usr.bin/find/find.h108
-rw-r--r--usr.bin/find/function.c1141
-rw-r--r--usr.bin/find/ls.c116
-rw-r--r--usr.bin/find/main.c156
-rw-r--r--usr.bin/find/misc.c127
-rw-r--r--usr.bin/find/operator.c276
-rw-r--r--usr.bin/find/option.c152
-rw-r--r--usr.bin/finger/Makefile6
-rw-r--r--usr.bin/finger/extern.h50
-rw-r--r--usr.bin/finger/finger.1209
-rw-r--r--usr.bin/finger/finger.c335
-rw-r--r--usr.bin/finger/finger.h65
-rw-r--r--usr.bin/finger/lprint.c362
-rw-r--r--usr.bin/finger/net.c185
-rw-r--r--usr.bin/finger/sprint.c174
-rw-r--r--usr.bin/finger/util.c405
-rw-r--r--usr.bin/fmt/Makefile7
-rw-r--r--usr.bin/fmt/fmt.195
-rw-r--r--usr.bin/fmt/fmt.c550
-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.191
-rw-r--r--usr.bin/from/from.c149
-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.c800
-rw-r--r--usr.bin/ftp/Makefile15
-rw-r--r--usr.bin/ftp/cmds.c2114
-rw-r--r--usr.bin/ftp/cmdtab.c227
-rw-r--r--usr.bin/ftp/complete.c367
-rw-r--r--usr.bin/ftp/domacro.c154
-rw-r--r--usr.bin/ftp/extern.h180
-rw-r--r--usr.bin/ftp/fetch.c608
-rw-r--r--usr.bin/ftp/ftp.11377
-rw-r--r--usr.bin/ftp/ftp.c1580
-rw-r--r--usr.bin/ftp/ftp_var.h169
-rw-r--r--usr.bin/ftp/main.c651
-rw-r--r--usr.bin/ftp/pathnames.h41
-rw-r--r--usr.bin/ftp/ruserpass.c302
-rw-r--r--usr.bin/ftp/util.c779
-rw-r--r--usr.bin/gcore/Makefile14
-rw-r--r--usr.bin/gcore/aoutcore.c314
-rw-r--r--usr.bin/gcore/extern.h37
-rw-r--r--usr.bin/gcore/gcore.195
-rw-r--r--usr.bin/gcore/gcore.c314
-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.c896
-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/global/HISTORY161
-rw-r--r--usr.bin/global/MANIFEST17
-rw-r--r--usr.bin/global/Makefile3
-rw-r--r--usr.bin/global/Makefile.inc1
-rw-r--r--usr.bin/global/README527
-rw-r--r--usr.bin/global/VERSION1
-rw-r--r--usr.bin/global/btreeop/Makefile5
-rw-r--r--usr.bin/global/btreeop/btreeop.1168
-rw-r--r--usr.bin/global/btreeop/btreeop.c370
-rw-r--r--usr.bin/global/gctags/C.c840
-rw-r--r--usr.bin/global/gctags/Makefile7
-rw-r--r--usr.bin/global/gctags/ctags.c404
-rw-r--r--usr.bin/global/gctags/ctags.h110
-rw-r--r--usr.bin/global/gctags/fortran.c168
-rw-r--r--usr.bin/global/gctags/gctags.1227
-rw-r--r--usr.bin/global/gctags/lisp.c105
-rw-r--r--usr.bin/global/gctags/print.c122
-rw-r--r--usr.bin/global/gctags/test/ctags.test67
-rw-r--r--usr.bin/global/gctags/tree.c145
-rw-r--r--usr.bin/global/gctags/yacc.c151
-rw-r--r--usr.bin/global/global/Makefile9
-rw-r--r--usr.bin/global/global/global.1136
-rw-r--r--usr.bin/global/global/global.pl232
-rw-r--r--usr.bin/global/gtags/Makefile9
-rw-r--r--usr.bin/global/gtags/gtags.186
-rw-r--r--usr.bin/global/gtags/gtags.sh98
-rw-r--r--usr.bin/global/htags/Makefile9
-rw-r--r--usr.bin/global/htags/htags.1117
-rwxr-xr-xusr.bin/global/htags/htags.pl1082
-rw-r--r--usr.bin/global/systags/Makefile7
-rwxr-xr-xusr.bin/global/systags/systags.sh83
-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.1293
-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.h351
-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.c733
-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/gprof4/Makefile14
-rw-r--r--usr.bin/head/Makefile5
-rw-r--r--usr.bin/head/head.172
-rw-r--r--usr.bin/head/head.c219
-rw-r--r--usr.bin/hexdump/Makefile10
-rw-r--r--usr.bin/hexdump/conv.c128
-rw-r--r--usr.bin/hexdump/display.c379
-rw-r--r--usr.bin/hexdump/hexdump.1343
-rw-r--r--usr.bin/hexdump/hexdump.c112
-rw-r--r--usr.bin/hexdump/hexdump.h98
-rw-r--r--usr.bin/hexdump/hexsyntax.c141
-rw-r--r--usr.bin/hexdump/od.182
-rw-r--r--usr.bin/hexdump/odsyntax.c265
-rw-r--r--usr.bin/hexdump/parse.c507
-rw-r--r--usr.bin/host/Makefile10
-rw-r--r--usr.bin/id/Makefile13
-rw-r--r--usr.bin/id/groups.163
-rw-r--r--usr.bin/id/groups.sh39
-rw-r--r--usr.bin/id/id.1145
-rw-r--r--usr.bin/id/id.c351
-rw-r--r--usr.bin/id/whoami.161
-rw-r--r--usr.bin/id/whoami.sh39
-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.c493
-rw-r--r--usr.bin/join/Makefile5
-rw-r--r--usr.bin/join/join.1215
-rw-r--r--usr.bin/join/join.c588
-rw-r--r--usr.bin/jot/Makefile5
-rw-r--r--usr.bin/jot/jot.1195
-rw-r--r--usr.bin/jot/jot.c398
-rw-r--r--usr.bin/kdump/Makefile12
-rw-r--r--usr.bin/kdump/kdump.1102
-rw-r--r--usr.bin/kdump/kdump.c457
-rw-r--r--usr.bin/kdump/mkioctls41
-rw-r--r--usr.bin/key/Makefile13
-rw-r--r--usr.bin/key/key.149
-rw-r--r--usr.bin/key/skey.c129
-rw-r--r--usr.bin/keyinfo/Makefile12
-rw-r--r--usr.bin/keyinfo/keyinfo.150
-rw-r--r--usr.bin/keyinfo/keyinfo.pl27
-rw-r--r--usr.bin/keyinit/Makefile15
-rw-r--r--usr.bin/keyinit/keyinit.164
-rw-r--r--usr.bin/keyinit/skeyinit.c191
-rw-r--r--usr.bin/keylogin/Makefile10
-rw-r--r--usr.bin/keylogin/keylogin.125
-rw-r--r--usr.bin/keylogin/keylogin.c80
-rw-r--r--usr.bin/keylogout/Makefile10
-rw-r--r--usr.bin/keylogout/keylogout.144
-rw-r--r--usr.bin/keylogout/keylogout.c68
-rw-r--r--usr.bin/killall/Makefile7
-rw-r--r--usr.bin/killall/killall.1134
-rwxr-xr-xusr.bin/killall/killall.pl117
-rw-r--r--usr.bin/ktrace/Makefile8
-rw-r--r--usr.bin/ktrace/ktrace.1173
-rw-r--r--usr.bin/ktrace/ktrace.c202
-rw-r--r--usr.bin/ktrace/ktrace.h41
-rw-r--r--usr.bin/ktrace/subr.c116
-rw-r--r--usr.bin/kzip/Makefile6
-rw-r--r--usr.bin/kzip/kzip.873
-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.c429
-rw-r--r--usr.bin/lastcomm/Makefile5
-rw-r--r--usr.bin/lastcomm/lastcomm.1156
-rw-r--r--usr.bin/lastcomm/lastcomm.c300
-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.147
-rw-r--r--usr.bin/ldd/ldd.c170
-rw-r--r--usr.bin/ldd/sods.c505
-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.h185
-rw-r--r--usr.bin/lex/Makefile59
-rw-r--r--usr.bin/lex/NEWS1233
-rw-r--r--usr.bin/lex/README60
-rw-r--r--usr.bin/lex/ccl.c149
-rw-r--r--usr.bin/lex/config.h26
-rw-r--r--usr.bin/lex/dfa.c1095
-rw-r--r--usr.bin/lex/ecs.c225
-rw-r--r--usr.bin/lex/flex.skl1541
-rw-r--r--usr.bin/lex/flexdef.h1048
-rw-r--r--usr.bin/lex/gen.c1625
-rw-r--r--usr.bin/lex/initscan.c3697
-rw-r--r--usr.bin/lex/lex.14062
-rw-r--r--usr.bin/lex/lib/Makefile16
-rw-r--r--usr.bin/lex/lib/libmain.c15
-rw-r--r--usr.bin/lex/lib/libyywrap.c8
-rw-r--r--usr.bin/lex/main.c1177
-rw-r--r--usr.bin/lex/misc.c886
-rwxr-xr-xusr.bin/lex/mkskel.sh16
-rw-r--r--usr.bin/lex/nfa.c709
-rw-r--r--usr.bin/lex/parse.y913
-rw-r--r--usr.bin/lex/scan.l710
-rw-r--r--usr.bin/lex/skel.c1548
-rw-r--r--usr.bin/lex/sym.c262
-rw-r--r--usr.bin/lex/tblcmp.c887
-rw-r--r--usr.bin/lex/version.h1
-rw-r--r--usr.bin/lex/yylex.c216
-rw-r--r--usr.bin/limits/Makefile13
-rw-r--r--usr.bin/limits/limits.1304
-rw-r--r--usr.bin/limits/limits.c630
-rw-r--r--usr.bin/locate/Makefile6
-rw-r--r--usr.bin/locate/Makefile.inc3
-rw-r--r--usr.bin/locate/bigram/Makefile9
-rw-r--r--usr.bin/locate/bigram/locate.bigram.c109
-rw-r--r--usr.bin/locate/code/Makefile9
-rw-r--r--usr.bin/locate/code/locate.code.c280
-rw-r--r--usr.bin/locate/locate/Makefile25
-rw-r--r--usr.bin/locate/locate/concatdb.sh72
-rw-r--r--usr.bin/locate/locate/fastfind.c332
-rw-r--r--usr.bin/locate/locate/locate.1249
-rw-r--r--usr.bin/locate/locate/locate.c371
-rw-r--r--usr.bin/locate/locate/locate.h72
-rw-r--r--usr.bin/locate/locate/locate.rc26
-rw-r--r--usr.bin/locate/locate/locate.updatedb.864
-rw-r--r--usr.bin/locate/locate/mklocatedb.sh74
-rw-r--r--usr.bin/locate/locate/pathnames.h36
-rw-r--r--usr.bin/locate/locate/updatedb.sh85
-rw-r--r--usr.bin/locate/locate/util.c279
-rw-r--r--usr.bin/lock/Makefile9
-rw-r--r--usr.bin/lock/lock.171
-rw-r--r--usr.bin/lock/lock.c243
-rw-r--r--usr.bin/lockf/Makefile6
-rw-r--r--usr.bin/lockf/lockf.1112
-rw-r--r--usr.bin/lockf/lockf.c213
-rw-r--r--usr.bin/logger/Makefile5
-rw-r--r--usr.bin/logger/logger.1100
-rw-r--r--usr.bin/logger/logger.c193
-rw-r--r--usr.bin/login/Makefile33
-rw-r--r--usr.bin/login/README20
-rw-r--r--usr.bin/login/klogin.c204
-rw-r--r--usr.bin/login/login.1179
-rw-r--r--usr.bin/login/login.access.550
-rw-r--r--usr.bin/login/login.c997
-rw-r--r--usr.bin/login/login_access.c239
-rw-r--r--usr.bin/login/login_fbtab.c153
-rw-r--r--usr.bin/login/pathnames.h43
-rw-r--r--usr.bin/logname/Makefile5
-rw-r--r--usr.bin/logname/logname.177
-rw-r--r--usr.bin/logname/logname.c (renamed from usr.bin/whereis/whereis.c)63
-rw-r--r--usr.bin/look/Makefile5
-rw-r--r--usr.bin/look/look.1105
-rw-r--r--usr.bin/look/look.c363
-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.c99
-rw-r--r--usr.bin/m4/Makefile10
-rw-r--r--usr.bin/m4/NOTES64
-rw-r--r--usr.bin/m4/PSD.doc/Makefile11
-rw-r--r--usr.bin/m4/TEST/ack.m442
-rw-r--r--usr.bin/m4/TEST/hanoi.m447
-rw-r--r--usr.bin/m4/TEST/hash.m457
-rw-r--r--usr.bin/m4/TEST/sqroot.m447
-rw-r--r--usr.bin/m4/TEST/string.m447
-rw-r--r--usr.bin/m4/TEST/test.m4245
-rw-r--r--usr.bin/m4/eval.c803
-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.h60
-rw-r--r--usr.bin/m4/serv.c475
-rw-r--r--usr.bin/m4/stdd.h56
-rw-r--r--usr.bin/mail/Makefile18
-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.c707
-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.11045
-rw-r--r--usr.bin/mail/main.c311
-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.c88
-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/Makefile8
-rw-r--r--usr.bin/make/PSD.doc/tutorial.ms3744
-rw-r--r--usr.bin/make/arch.c1234
-rw-r--r--usr.bin/make/buf.c463
-rw-r--r--usr.bin/make/buf.h82
-rw-r--r--usr.bin/make/compat.c654
-rw-r--r--usr.bin/make/cond.c1256
-rw-r--r--usr.bin/make/config.h117
-rw-r--r--usr.bin/make/dir.c1276
-rw-r--r--usr.bin/make/dir.h71
-rw-r--r--usr.bin/make/for.c301
-rw-r--r--usr.bin/make/hash.c420
-rw-r--r--usr.bin/make/hash.h117
-rw-r--r--usr.bin/make/job.c3107
-rw-r--r--usr.bin/make/job.h234
-rw-r--r--usr.bin/make/list.h299
-rw-r--r--usr.bin/make/lst.h165
-rw-r--r--usr.bin/make/lst.lib/lstAppend.c115
-rw-r--r--usr.bin/make/lst.lib/lstAtEnd.c72
-rw-r--r--usr.bin/make/lst.lib/lstAtFront.c73
-rw-r--r--usr.bin/make/lst.lib/lstClose.c79
-rw-r--r--usr.bin/make/lst.lib/lstConcat.c178
-rw-r--r--usr.bin/make/lst.lib/lstDatum.c73
-rw-r--r--usr.bin/make/lst.lib/lstDeQueue.c83
-rw-r--r--usr.bin/make/lst.lib/lstDestroy.c104
-rw-r--r--usr.bin/make/lst.lib/lstDupl.c101
-rw-r--r--usr.bin/make/lst.lib/lstEnQueue.c75
-rw-r--r--usr.bin/make/lst.lib/lstFind.c72
-rw-r--r--usr.bin/make/lst.lib/lstFindFrom.c96
-rw-r--r--usr.bin/make/lst.lib/lstFirst.c73
-rw-r--r--usr.bin/make/lst.lib/lstForEach.c74
-rw-r--r--usr.bin/make/lst.lib/lstForEachFrom.c114
-rw-r--r--usr.bin/make/lst.lib/lstInit.c78
-rw-r--r--usr.bin/make/lst.lib/lstInsert.c115
-rw-r--r--usr.bin/make/lst.lib/lstInt.h112
-rw-r--r--usr.bin/make/lst.lib/lstIsAtEnd.c83
-rw-r--r--usr.bin/make/lst.lib/lstIsEmpty.c71
-rw-r--r--usr.bin/make/lst.lib/lstLast.c73
-rw-r--r--usr.bin/make/lst.lib/lstMember.c71
-rw-r--r--usr.bin/make/lst.lib/lstNext.c116
-rw-r--r--usr.bin/make/lst.lib/lstOpen.c83
-rw-r--r--usr.bin/make/lst.lib/lstRemove.c133
-rw-r--r--usr.bin/make/lst.lib/lstReplace.c75
-rw-r--r--usr.bin/make/lst.lib/lstSucc.c75
-rw-r--r--usr.bin/make/main.c1255
-rw-r--r--usr.bin/make/make.1966
-rw-r--r--usr.bin/make/make.c912
-rw-r--r--usr.bin/make/make.h372
-rw-r--r--usr.bin/make/nonints.h145
-rw-r--r--usr.bin/make/parse.c2596
-rw-r--r--usr.bin/make/pathnames.h41
-rw-r--r--usr.bin/make/sprite.h112
-rw-r--r--usr.bin/make/str.c492
-rw-r--r--usr.bin/make/suff.c2449
-rw-r--r--usr.bin/make/targ.c660
-rw-r--r--usr.bin/make/util.c349
-rw-r--r--usr.bin/make/var.c2044
-rw-r--r--usr.bin/makewhatis/makewhatis.local.870
-rw-r--r--usr.bin/makewhatis/makewhatis.local.sh58
-rw-r--r--usr.bin/man/Makefile8
-rw-r--r--usr.bin/man/config.c176
-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.192
-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.c97
-rw-r--r--usr.bin/mk_cmds/options.c32
-rw-r--r--usr.bin/mk_cmds/utils.c135
-rw-r--r--usr.bin/mkdep/Makefile9
-rw-r--r--usr.bin/mkdep/mkdep.1103
-rw-r--r--usr.bin/mkdep/mkdep.gcc.sh100
-rw-r--r--usr.bin/mkdep/mkdep.sh111
-rw-r--r--usr.bin/mkfifo/Makefile5
-rw-r--r--usr.bin/mkfifo/mkfifo.177
-rw-r--r--usr.bin/mkfifo/mkfifo.c82
-rw-r--r--usr.bin/mklocale/Japanese158
-rw-r--r--usr.bin/mklocale/Makefile10
-rw-r--r--usr.bin/mklocale/README.locale_name7
-rw-r--r--usr.bin/mklocale/data/Makefile50
-rw-r--r--usr.bin/mklocale/data/ja_JP.EUC.src158
-rw-r--r--usr.bin/mklocale/data/ko_KR.EUC.src119
-rw-r--r--usr.bin/mklocale/data/lt_LN.ASCII.src28
-rw-r--r--usr.bin/mklocale/data/lt_LN.ISO_8859-1.src39
-rw-r--r--usr.bin/mklocale/data/lt_LN.ISO_8859-2.src79
-rw-r--r--usr.bin/mklocale/data/ru_SU.CP866.src42
-rw-r--r--usr.bin/mklocale/data/ru_SU.KOI8-R.src39
-rw-r--r--usr.bin/mklocale/ldef.h53
-rw-r--r--usr.bin/mklocale/lex.l152
-rw-r--r--usr.bin/mklocale/mklocale.1258
-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/Makefile42
-rw-r--r--usr.bin/modstat/modstat.869
-rw-r--r--usr.bin/modstat/modstat.c180
-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.c372
-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.c258
-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.c863
-rw-r--r--usr.bin/msgs/pathnames.h40
-rw-r--r--usr.bin/mt/Makefile6
-rw-r--r--usr.bin/mt/mt.1226
-rw-r--r--usr.bin/mt/mt.c467
-rw-r--r--usr.bin/netstat/Makefile14
-rw-r--r--usr.bin/netstat/atalk.c285
-rw-r--r--usr.bin/netstat/if.c501
-rw-r--r--usr.bin/netstat/inet.c521
-rw-r--r--usr.bin/netstat/ipx.c364
-rw-r--r--usr.bin/netstat/iso.c843
-rw-r--r--usr.bin/netstat/main.c611
-rw-r--r--usr.bin/netstat/mbuf.c134
-rw-r--r--usr.bin/netstat/mroute.c234
-rw-r--r--usr.bin/netstat/netstat.1299
-rw-r--r--usr.bin/netstat/netstat.h125
-rw-r--r--usr.bin/netstat/ns.c351
-rw-r--r--usr.bin/netstat/route.c861
-rw-r--r--usr.bin/netstat/unix.c135
-rw-r--r--usr.bin/newkey/Makefile13
-rw-r--r--usr.bin/newkey/generic.c135
-rw-r--r--usr.bin/newkey/newkey.857
-rw-r--r--usr.bin/newkey/newkey.c235
-rw-r--r--usr.bin/newkey/update.c356
-rw-r--r--usr.bin/nfsstat/Makefile10
-rw-r--r--usr.bin/nfsstat/nfsstat.191
-rw-r--r--usr.bin/nfsstat/nfsstat.c405
-rw-r--r--usr.bin/nice/Makefile5
-rw-r--r--usr.bin/nice/nice.1118
-rw-r--r--usr.bin/nice/nice.c95
-rw-r--r--usr.bin/nm/Makefile5
-rw-r--r--usr.bin/nm/nm.1126
-rw-r--r--usr.bin/nm/nm.1aout126
-rw-r--r--usr.bin/nm/nm.c684
-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/opieinfo/Makefile16
-rw-r--r--usr.bin/opiekey/Makefile16
-rw-r--r--usr.bin/opiepasswd/Makefile16
-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/Makefile79
-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.c225
-rw-r--r--usr.bin/passwd/passwd.1210
-rw-r--r--usr.bin/passwd/passwd.c243
-rw-r--r--usr.bin/passwd/yp_passwd.c188
-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.1353
-rw-r--r--usr.bin/pr/pr.c1810
-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.c100
-rw-r--r--usr.bin/printf/Makefile5
-rw-r--r--usr.bin/printf/printf.1272
-rw-r--r--usr.bin/printf/printf.c409
-rw-r--r--usr.bin/quota/Makefile10
-rw-r--r--usr.bin/quota/quota.1143
-rw-r--r--usr.bin/quota/quota.c734
-rw-r--r--usr.bin/ranlib/Makefile10
-rw-r--r--usr.bin/ranlib/build.c296
-rw-r--r--usr.bin/ranlib/misc.c110
-rw-r--r--usr.bin/ranlib/pathnames.h36
-rw-r--r--usr.bin/ranlib/ranlib.189
-rw-r--r--usr.bin/ranlib/ranlib.1aout89
-rw-r--r--usr.bin/ranlib/ranlib.570
-rw-r--r--usr.bin/ranlib/ranlib.5.570
-rw-r--r--usr.bin/ranlib/ranlib.c96
-rw-r--r--usr.bin/ranlib/touch.c91
-rw-r--r--usr.bin/rdist/Makefile16
-rw-r--r--usr.bin/rdist/cron.entry1
-rw-r--r--usr.bin/rdist/defs.h190
-rw-r--r--usr.bin/rdist/docmd.c694
-rw-r--r--usr.bin/rdist/expand.c672
-rw-r--r--usr.bin/rdist/gram.y519
-rw-r--r--usr.bin/rdist/lookup.c167
-rw-r--r--usr.bin/rdist/main.c335
-rw-r--r--usr.bin/rdist/pathnames.h39
-rw-r--r--usr.bin/rdist/rdist.1419
-rw-r--r--usr.bin/rdist/rshrcmd.c124
-rw-r--r--usr.bin/rdist/server.c1594
-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.c110
-rw-r--r--usr.bin/rlogin/Makefile19
-rw-r--r--usr.bin/rlogin/des_rw.c203
-rw-r--r--usr.bin/rlogin/kcmd.c314
-rw-r--r--usr.bin/rlogin/krb.h38
-rw-r--r--usr.bin/rlogin/krcmd.c158
-rw-r--r--usr.bin/rlogin/rlogin.1205
-rw-r--r--usr.bin/rlogin/rlogin.c954
-rw-r--r--usr.bin/rpcgen/Makefile13
-rw-r--r--usr.bin/rpcgen/rpc_clntout.c330
-rw-r--r--usr.bin/rpcgen/rpc_cout.c758
-rw-r--r--usr.bin/rpcgen/rpc_hout.c599
-rw-r--r--usr.bin/rpcgen/rpc_main.c1388
-rw-r--r--usr.bin/rpcgen/rpc_parse.c656
-rw-r--r--usr.bin/rpcgen/rpc_parse.h197
-rw-r--r--usr.bin/rpcgen/rpc_sample.c312
-rw-r--r--usr.bin/rpcgen/rpc_scan.c517
-rw-r--r--usr.bin/rpcgen/rpc_scan.h133
-rw-r--r--usr.bin/rpcgen/rpc_svcout.c1086
-rw-r--r--usr.bin/rpcgen/rpc_tblout.c172
-rw-r--r--usr.bin/rpcgen/rpc_util.c517
-rw-r--r--usr.bin/rpcgen/rpc_util.h213
-rw-r--r--usr.bin/rpcgen/rpcgen.1552
-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.c547
-rw-r--r--usr.bin/rsh/Makefile20
-rw-r--r--usr.bin/rsh/pathnames.h36
-rw-r--r--usr.bin/rsh/rsh.1193
-rw-r--r--usr.bin/rsh/rsh.c496
-rw-r--r--usr.bin/rup/Makefile9
-rw-r--r--usr.bin/rup/rup.194
-rw-r--r--usr.bin/rup/rup.c231
-rw-r--r--usr.bin/ruptime/Makefile5
-rw-r--r--usr.bin/ruptime/ruptime.182
-rw-r--r--usr.bin/ruptime/ruptime.c284
-rw-r--r--usr.bin/rusers/Makefile9
-rw-r--r--usr.bin/rusers/rusers.193
-rw-r--r--usr.bin/rusers/rusers.c252
-rw-r--r--usr.bin/rwall/Makefile9
-rw-r--r--usr.bin/rwall/rwall.179
-rw-r--r--usr.bin/rwall/rwall.c177
-rw-r--r--usr.bin/rwho/Makefile5
-rw-r--r--usr.bin/rwho/rwho.180
-rw-r--r--usr.bin/rwho/rwho.c192
-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.c197
-rw-r--r--usr.bin/sccs/Makefile5
-rw-r--r--usr.bin/sccs/PSD.doc/Makefile7
-rw-r--r--usr.bin/sccs/PSD.doc/sccs.me1609
-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.c355
-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.1172
-rwxr-xr-xusr.bin/sgmlfmt/sgmlfmt.pl749
-rw-r--r--usr.bin/sgmls/LICENSE43
-rw-r--r--usr.bin/sgmls/Makefile9
-rw-r--r--usr.bin/sgmls/Makefile.inc16
-rw-r--r--usr.bin/sgmls/README138
-rwxr-xr-xusr.bin/sgmls/configure617
-rw-r--r--usr.bin/sgmls/instant/Makefile15
-rw-r--r--usr.bin/sgmls/instant/README150
-rw-r--r--usr.bin/sgmls/instant/browse.c462
-rw-r--r--usr.bin/sgmls/instant/general.h329
-rw-r--r--usr.bin/sgmls/instant/hyper.c100
-rw-r--r--usr.bin/sgmls/instant/info.c300
-rw-r--r--usr.bin/sgmls/instant/instant.1183
-rw-r--r--usr.bin/sgmls/instant/main.c710
-rw-r--r--usr.bin/sgmls/instant/tables.c2013
-rw-r--r--usr.bin/sgmls/instant/traninit.c577
-rw-r--r--usr.bin/sgmls/instant/translate.c881
-rw-r--r--usr.bin/sgmls/instant/translate.h153
-rw-r--r--usr.bin/sgmls/instant/transpec.5528
-rw-r--r--usr.bin/sgmls/instant/tranvar.c763
-rw-r--r--usr.bin/sgmls/instant/util.c1109
-rw-r--r--usr.bin/sgmls/libsgmls/Makefile17
-rw-r--r--usr.bin/sgmls/libsgmls/sgmls.c1032
-rw-r--r--usr.bin/sgmls/libsgmls/sgmls.h127
-rwxr-xr-xusr.bin/sgmls/sgmls.pl247
-rw-r--r--usr.bin/sgmls/sgmls/Makefile19
-rw-r--r--usr.bin/sgmls/sgmls/action.h180
-rw-r--r--usr.bin/sgmls/sgmls/adl.h118
-rw-r--r--usr.bin/sgmls/sgmls/alloc.h8
-rw-r--r--usr.bin/sgmls/sgmls/ambig.c438
-rw-r--r--usr.bin/sgmls/sgmls/appl.h31
-rw-r--r--usr.bin/sgmls/sgmls/catalog.c925
-rw-r--r--usr.bin/sgmls/sgmls/catalog.h45
-rw-r--r--usr.bin/sgmls/sgmls/config.h158
-rw-r--r--usr.bin/sgmls/sgmls/context.c451
-rw-r--r--usr.bin/sgmls/sgmls/context.h19
-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.h25
-rw-r--r--usr.bin/sgmls/sgmls/entgen.c517
-rw-r--r--usr.bin/sgmls/sgmls/entity.h192
-rw-r--r--usr.bin/sgmls/sgmls/error.h61
-rw-r--r--usr.bin/sgmls/sgmls/etype.h93
-rw-r--r--usr.bin/sgmls/sgmls/exclude.c121
-rw-r--r--usr.bin/sgmls/sgmls/genlex.c140
-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.h37
-rw-r--r--usr.bin/sgmls/sgmls/lexcode.h12
-rw-r--r--usr.bin/sgmls/sgmls/lexrf.c125
-rw-r--r--usr.bin/sgmls/sgmls/lextaba.c750
-rw-r--r--usr.bin/sgmls/sgmls/lextabe.c357
-rw-r--r--usr.bin/sgmls/sgmls/lextoke.h10
-rw-r--r--usr.bin/sgmls/sgmls/lineout.c656
-rw-r--r--usr.bin/sgmls/sgmls/lineout.h23
-rw-r--r--usr.bin/sgmls/sgmls/main.c650
-rw-r--r--usr.bin/sgmls/sgmls/md1.c866
-rw-r--r--usr.bin/sgmls/sgmls/md2.c792
-rw-r--r--usr.bin/sgmls/sgmls/msg.h258
-rw-r--r--usr.bin/sgmls/sgmls/msgcat.c840
-rw-r--r--usr.bin/sgmls/sgmls/msgcat.h13
-rw-r--r--usr.bin/sgmls/sgmls/pars1.c984
-rw-r--r--usr.bin/sgmls/sgmls/pars2.c1333
-rw-r--r--usr.bin/sgmls/sgmls/pcbrf.c1351
-rw-r--r--usr.bin/sgmls/sgmls/portproc.c105
-rw-r--r--usr.bin/sgmls/sgmls/serv.c299
-rw-r--r--usr.bin/sgmls/sgmls/sgml1.c485
-rw-r--r--usr.bin/sgmls/sgmls/sgml2.c522
-rw-r--r--usr.bin/sgmls/sgmls/sgmlaux.h72
-rw-r--r--usr.bin/sgmls/sgmls/sgmldecl.c1804
-rw-r--r--usr.bin/sgmls/sgmls/sgmldecl.h90
-rw-r--r--usr.bin/sgmls/sgmls/sgmlfnsm.h130
-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.1970
-rw-r--r--usr.bin/sgmls/sgmls/sgmlxtrn.c225
-rw-r--r--usr.bin/sgmls/sgmls/sgmlxtrn.h123
-rw-r--r--usr.bin/sgmls/sgmls/source.h114
-rw-r--r--usr.bin/sgmls/sgmls/std.h110
-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.h154
-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.c462
-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.c564
-rw-r--r--usr.bin/sgmls/unix.cfg165
-rw-r--r--usr.bin/shar/Makefile9
-rw-r--r--usr.bin/shar/shar.1110
-rw-r--r--usr.bin/shar/shar.sh74
-rw-r--r--usr.bin/showmount/Makefile6
-rw-r--r--usr.bin/showmount/showmount.894
-rw-r--r--usr.bin/showmount/showmount.c356
-rw-r--r--usr.bin/size/Makefile5
-rw-r--r--usr.bin/size/size.161
-rw-r--r--usr.bin/size/size.1aout61
-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/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.c221
-rw-r--r--usr.bin/strip/Makefile21
-rw-r--r--usr.bin/strip/strip.173
-rw-r--r--usr.bin/strip/strip.1aout73
-rw-r--r--usr.bin/strip/strip.c278
-rw-r--r--usr.bin/su/Makefile37
-rw-r--r--usr.bin/su/su.1210
-rw-r--r--usr.bin/su/su.c564
-rw-r--r--usr.bin/symorder/Makefile5
-rw-r--r--usr.bin/symorder/symorder.196
-rw-r--r--usr.bin/symorder/symorder.c354
-rw-r--r--usr.bin/systat/Makefile12
-rw-r--r--usr.bin/systat/cmds.c196
-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.c296
-rw-r--r--usr.bin/systat/mbufs.c186
-rw-r--r--usr.bin/systat/netcmds.c312
-rw-r--r--usr.bin/systat/netstat.c466
-rw-r--r--usr.bin/systat/pigs.c245
-rw-r--r--usr.bin/systat/swap.c267
-rw-r--r--usr.bin/systat/systat.1426
-rw-r--r--usr.bin/systat/systat.h60
-rw-r--r--usr.bin/systat/vmstat.c680
-rw-r--r--usr.bin/tail/Makefile6
-rw-r--r--usr.bin/tail/extern.h52
-rw-r--r--usr.bin/tail/forward.c236
-rw-r--r--usr.bin/tail/misc.c62
-rw-r--r--usr.bin/tail/read.c201
-rw-r--r--usr.bin/tail/reverse.c265
-rw-r--r--usr.bin/tail/tail.1165
-rw-r--r--usr.bin/tail/tail.c303
-rw-r--r--usr.bin/talk/Makefile11
-rw-r--r--usr.bin/talk/ctl.c116
-rw-r--r--usr.bin/talk/ctl_transact.c113
-rw-r--r--usr.bin/talk/display.c179
-rw-r--r--usr.bin/talk/get_addrs.c72
-rw-r--r--usr.bin/talk/get_iface.c99
-rw-r--r--usr.bin/talk/get_names.c116
-rw-r--r--usr.bin/talk/init_disp.c180
-rw-r--r--usr.bin/talk/invite.c194
-rw-r--r--usr.bin/talk/io.c145
-rw-r--r--usr.bin/talk/look_up.c118
-rw-r--r--usr.bin/talk/msgs.c82
-rw-r--r--usr.bin/talk/talk.1133
-rw-r--r--usr.bin/talk/talk.c83
-rw-r--r--usr.bin/talk/talk.h94
-rw-r--r--usr.bin/talk/talk_ctl.h43
-rw-r--r--usr.bin/tclsh/Makefile20
-rw-r--r--usr.bin/tconv/Makefile12
-rw-r--r--usr.bin/tconv/quit.c72
-rw-r--r--usr.bin/tconv/tconv.1179
-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/Makefile61
-rw-r--r--usr.bin/telnet/README743
-rw-r--r--usr.bin/telnet/authenc.c105
-rw-r--r--usr.bin/telnet/commands.c2822
-rw-r--r--usr.bin/telnet/defines.h61
-rw-r--r--usr.bin/telnet/externs.h478
-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.c1191
-rw-r--r--usr.bin/telnet/telnet.11368
-rw-r--r--usr.bin/telnet/telnet.c2560
-rw-r--r--usr.bin/telnet/terminal.c223
-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.c738
-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.197
-rw-r--r--usr.bin/time/time.c173
-rw-r--r--usr.bin/tip/Makefile5
-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/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/Makefile25
-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.c112
-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.1442
-rw-r--r--usr.bin/tip/tip/tip.c688
-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/value.c353
-rw-r--r--usr.bin/tip/tip/vars.c117
-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/Makefile40
-rw-r--r--usr.bin/tn3270/mset/map3270.5343
-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.c948
-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/top/Makefile28
-rw-r--r--usr.bin/top/machine.c935
-rw-r--r--usr.bin/top/sigdesc.h42
-rw-r--r--usr.bin/top/top.local.147
-rw-r--r--usr.bin/top/top.local.h68
-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.1136
-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.c339
-rw-r--r--usr.bin/tr/tr.1292
-rw-r--r--usr.bin/tr/tr.c293
-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.191
-rw-r--r--usr.bin/tsort/tsort.c437
-rw-r--r--usr.bin/tty/Makefile5
-rw-r--r--usr.bin/tty/tty.184
-rw-r--r--usr.bin/tty/tty.c69
-rw-r--r--usr.bin/ul/Makefile7
-rw-r--r--usr.bin/ul/ul.1108
-rw-r--r--usr.bin/ul/ul.c508
-rw-r--r--usr.bin/uname/Makefile5
-rw-r--r--usr.bin/uname/uname.198
-rw-r--r--usr.bin/uname/uname.c163
-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.c276
-rw-r--r--usr.bin/units/Makefile2
-rw-r--r--usr.bin/units/README2
-rw-r--r--usr.bin/units/pathnames.h2
-rw-r--r--usr.bin/units/units.184
-rw-r--r--usr.bin/units/units.c8
-rw-r--r--usr.bin/units/units.lib2
-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/acucntrl.c814
-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/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.c273
-rw-r--r--usr.bin/uuencode/Makefile9
-rw-r--r--usr.bin/uuencode/uuencode.1132
-rw-r--r--usr.bin/uuencode/uuencode.c150
-rw-r--r--usr.bin/uuencode/uuencode.format.5104
-rw-r--r--usr.bin/vacation/Makefile7
-rw-r--r--usr.bin/vacation/vacation.1179
-rw-r--r--usr.bin/vacation/vacation.c486
-rw-r--r--usr.bin/vgrind/Makefile27
-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.c598
-rw-r--r--usr.bin/vgrind/tmac.vgrind68
-rw-r--r--usr.bin/vgrind/vfontedpr.c724
-rw-r--r--usr.bin/vgrind/vgrind.1236
-rw-r--r--usr.bin/vgrind/vgrind.sh155
-rw-r--r--usr.bin/vgrind/vgrindefs.5169
-rw-r--r--usr.bin/vgrind/vgrindefs.c326
-rw-r--r--usr.bin/vgrind/vgrindefs.src159
-rw-r--r--usr.bin/vi/Makefile202
-rw-r--r--usr.bin/vi/config.h194
-rw-r--r--usr.bin/vi/pathnames.h45
-rw-r--r--usr.bin/vi/port.h185
-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.c176
-rw-r--r--usr.bin/vmstat/Makefile11
-rw-r--r--usr.bin/vmstat/names.c271
-rw-r--r--usr.bin/vmstat/vmstat.8219
-rw-r--r--usr.bin/vmstat/vmstat.c837
-rw-r--r--usr.bin/w/Makefile14
-rw-r--r--usr.bin/w/extern.h39
-rw-r--r--usr.bin/w/pr_time.c113
-rw-r--r--usr.bin/w/proc_compare.c120
-rw-r--r--usr.bin/w/uptime.160
-rw-r--r--usr.bin/w/w.1142
-rw-r--r--usr.bin/w/w.c458
-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.c218
-rw-r--r--usr.bin/wc/Makefile5
-rw-r--r--usr.bin/wc/wc.1115
-rw-r--r--usr.bin/wc/wc.c241
-rw-r--r--usr.bin/what/Makefile5
-rw-r--r--usr.bin/what/what.173
-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/Makefile7
-rw-r--r--usr.bin/whereis/whereis.1100
-rw-r--r--usr.bin/whereis/whereis.pl243
-rw-r--r--usr.bin/which/Makefile9
-rw-r--r--usr.bin/which/which.165
-rwxr-xr-xusr.bin/which/which.pl55
-rw-r--r--usr.bin/who/Makefile5
-rw-r--r--usr.bin/who/who.1106
-rw-r--r--usr.bin/who/who.c139
-rw-r--r--usr.bin/whois/Makefile12
-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.1104
-rw-r--r--usr.bin/write/write.c331
-rw-r--r--usr.bin/xargs/Makefile5
-rw-r--r--usr.bin/xargs/pathnames.h36
-rw-r--r--usr.bin/xargs/xargs.1172
-rw-r--r--usr.bin/xargs/xargs.c342
-rw-r--r--usr.bin/xinstall/Makefile12
-rw-r--r--usr.bin/xinstall/install.1175
-rw-r--r--usr.bin/xinstall/pathnames.h36
-rw-r--r--usr.bin/xinstall/xinstall.c711
-rw-r--r--usr.bin/xlint/Makefile5
-rw-r--r--usr.bin/xlint/lint1/Makefile19
-rw-r--r--usr.bin/xlint/lint1/cgram.y1686
-rw-r--r--usr.bin/xlint/lint1/decl.c3135
-rw-r--r--usr.bin/xlint/lint1/emit.c241
-rw-r--r--usr.bin/xlint/lint1/emit1.c587
-rw-r--r--usr.bin/xlint/lint1/err.c543
-rw-r--r--usr.bin/xlint/lint1/externs.h56
-rw-r--r--usr.bin/xlint/lint1/externs1.h281
-rw-r--r--usr.bin/xlint/lint1/func.c1261
-rw-r--r--usr.bin/xlint/lint1/init.c513
-rw-r--r--usr.bin/xlint/lint1/lint.h118
-rw-r--r--usr.bin/xlint/lint1/lint1.h380
-rw-r--r--usr.bin/xlint/lint1/main1.c183
-rw-r--r--usr.bin/xlint/lint1/mem.c91
-rw-r--r--usr.bin/xlint/lint1/mem1.c358
-rw-r--r--usr.bin/xlint/lint1/op.h120
-rw-r--r--usr.bin/xlint/lint1/param.h120
-rw-r--r--usr.bin/xlint/lint1/scan.l1427
-rw-r--r--usr.bin/xlint/lint1/tree.c3927
-rw-r--r--usr.bin/xlint/lint2/Makefile13
-rw-r--r--usr.bin/xlint/lint2/chk.c1462
-rw-r--r--usr.bin/xlint/lint2/emit2.c236
-rw-r--r--usr.bin/xlint/lint2/externs2.h87
-rw-r--r--usr.bin/xlint/lint2/hash.c123
-rw-r--r--usr.bin/xlint/lint2/lint2.h177
-rw-r--r--usr.bin/xlint/lint2/main2.c190
-rw-r--r--usr.bin/xlint/lint2/mem2.c97
-rw-r--r--usr.bin/xlint/lint2/msg.c157
-rw-r--r--usr.bin/xlint/lint2/read.c1135
-rw-r--r--usr.bin/xlint/llib/Makefile21
-rw-r--r--usr.bin/xlint/llib/llib-lposix311
-rw-r--r--usr.bin/xlint/llib/llib-lstdc252
-rw-r--r--usr.bin/xlint/xlint/Makefile17
-rw-r--r--usr.bin/xlint/xlint/lint.1509
-rw-r--r--usr.bin/xlint/xlint/pathnames.h38
-rw-r--r--usr.bin/xlint/xlint/xlint.c771
-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/Makefile15
-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.c312
-rw-r--r--usr.bin/yacc/defs.h362
-rw-r--r--usr.bin/yacc/error.c392
-rw-r--r--usr.bin/yacc/lalr.c711
-rw-r--r--usr.bin/yacc/lr0.c673
-rw-r--r--usr.bin/yacc/main.c488
-rw-r--r--usr.bin/yacc/mkpar.c413
-rw-r--r--usr.bin/yacc/output.c1303
-rw-r--r--usr.bin/yacc/reader.c1878
-rw-r--r--usr.bin/yacc/skeleton.c395
-rw-r--r--usr.bin/yacc/symtab.c166
-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.c391
-rw-r--r--usr.bin/yacc/warshall.c128
-rw-r--r--usr.bin/yacc/yacc.1158
-rw-r--r--usr.bin/yacc/yyfix.1114
-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.c135
-rw-r--r--usr.bin/ypwhich/Makefile7
-rw-r--r--usr.bin/ypwhich/ypwhich.c256
1872 files changed, 447576 insertions, 96 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
new file mode 100644
index 0000000..5f9e8a2
--- /dev/null
+++ b/usr.bin/Makefile
@@ -0,0 +1,68 @@
+# From: @(#)Makefile 8.3 (Berkeley) 1/7/94
+# $Id: Makefile,v 1.85 1997/05/29 15:03:32 wpaul 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 brandelf cal calendar \
+ cap_mkdb chat checknr chflags chpass cksum col colcrt colldef colrm \
+ column comm compile_et compress chkey cpp ctags cut dig \
+ dirname dnsquery du ee env error expand f2c false fetch file file2c \
+ find finger fmt fold fpr from fsplit fstat ftp gcore gencat getopt \
+ global gprof head hexdump host id indent ipcrm ipcs \
+ join jot kdump ktrace key keyinfo keyinit keylogin keylogout killall \
+ lam last lastcomm leave lex limits locate lock lockf logger login \
+ logname lorder lsvfs m4 mail make mesg mkdep mkfifo mklocale mkstr \
+ mk_cmds modstat more msgs mt netstat newkey nfsstat nice \
+ nm nohup opieinfo opiekey opiepasswd 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 top touch tput tr true tset tsort tty ul uname \
+ unexpand unifdef uniq units unvis users uudecode uuencode vacation \
+ vgrind vi vis w wall wc what whereis which who whois window \
+ write xargs xinstall xlint xstr yacc yes ypcat ypmatch ypwhich
+SUBDIR+=gprof4
+
+.if !defined(NOTCL) && exists (${.CURDIR}/../contrib/tcl) && \
+ exists(${.CURDIR}/tclsh) && exists (${.CURDIR}/../lib/libtcl)
+SUBDIR+=tclsh
+.endif
+
+.if !exists(${.CURDIR}/../eBones) || defined(NOSECURE) || !defined(MAKE_EBONES)
+SUBDIR+=telnet
+.else
+.if defined(RELEASEDIR)
+# releases do need both
+SUBDIR+=telnet
+.endif
+SUBDIR+= ../eBones/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 sasc 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..9ab0a07
--- /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 #
+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..78e9062
--- /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")) != -1)
+ 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..112aada
--- /dev/null
+++ b/usr.bin/apropos/apropos.c
@@ -0,0 +1,224 @@
+/*
+ * 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.8 (Berkeley) 5/4/95";
+#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 <unistd.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..ea2e04c
--- /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..e9118e7
--- /dev/null
+++ b/usr.bin/ar/ar.1
@@ -0,0 +1,318 @@
+.\" 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
+.\" $Id: ar.1,v 1.4 1997/02/22 19:54:01 peter Exp $
+.\"
+.Dd June 29, 1993
+.Dt AR 1
+.Sh NAME
+.Nm ar
+.Nd create and maintain library archives
+.Sh SYNOPSIS
+.Nm ar
+.Fl d
+.Op Fl \Tv
+.Ar archive file ...
+.Nm ar
+.Fl m
+.Op Fl \Tv
+.Ar archive file ...
+.Nm ar
+.Fl m
+.Op Fl abiTv
+.Ar position archive file ...
+.Nm ar
+.Fl p
+.Op Fl \Tv
+.Ar archive [file ...]
+.Nm ar
+.Fl q
+.Op Fl cTv
+.Ar archive file ...
+.Nm ar
+.Fl r
+.Op Fl cuTv
+.Ar archive file ...
+.Nm ar
+.Fl r
+.Op Fl abciuTv
+.Ar position archive file ...
+.Nm ar
+.Fl t
+.Op Fl \Tv
+.Ar archive [file ...]
+.Nm ar
+.Fl x
+.Op Fl ouTv
+.Ar archive [file ...]
+.Sh DESCRIPTION
+The
+.Nm 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
+.Em first
+file with a matching name will be selected.
+.Pp
+The normal use of
+.Nm ar
+is for the creation and maintenance of libraries suitable for use with
+the loader (see
+.Xr ld 1 )
+although it is not restricted to this purpose.
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+A positioning modifier used with the options
+.Fl r
+and
+.Fl m .
+The files are entered or moved
+.Em after
+the archive member
+.Ar position ,
+which must be specified.
+.It Fl b
+A positioning modifier used with the options
+.Fl r
+and
+.Fl m .
+The files are entered or moved
+.Em before
+the archive member
+.Ar position ,
+which must be specified.
+.It Fl c
+Whenever an archive is created, an informational message to that effect
+is written to standard error.
+If the
+.Fl c
+option is specified,
+.Nm ar
+creates the archive silently.
+.It Fl d
+Delete the specified archive files.
+.It Fl i
+Identical to the
+.Fl b
+option.
+.It Fl m
+Move the specified archive files within the archive.
+If one of the options
+.Fl a ,
+.Fl b
+or
+.Fl i
+is specified, the files are moved
+before or after the
+.Ar position
+file in the archive.
+If none of those options are specified, the files are moved
+to the end of the archive.
+.It Fl 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.
+.It Fl 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.
+.It Fl 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
+.Fl 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.
+.It Fl 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
+.Fl a ,
+.Fl b
+or
+.Fl i
+is specified.
+.It Fl 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
+.Xr ar 5
+for more information.)
+.It Fl 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.
+.It Fl u
+Update files.
+When used with the
+.Fl 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
+.Fl x
+option, files in the archive will be extracted
+only if the archive file has a newer modification time than the file
+on disk.
+.It Fl v
+Provide verbose output.
+When used with the
+.Fl d ,
+.Fl m ,
+.Fl q
+or
+.Fl x
+options,
+.Nm 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
+.Fl r
+option,
+.Nm 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.
+.Pp
+When used with the
+.Fl 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.
+.Pp
+When used with the
+.Fl t
+option,
+.Nm 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
+.Xr 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
+.Xr date 1
+format ``%b %e %H:%M %Y''), and the name of the file.
+.It Fl 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.
+.Pp
+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
+.Fl 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.
+.El
+.Pp
+The
+.Nm ar
+utility exits 0 on success, and >0 if an error occurs.
+.Sh ENVIRONMENT
+.Bl -tag -width indent -compact
+.It Ev TMPDIR
+The pathname of the directory to use when creating temporary files.
+.El
+.Sh FILES
+.Bl -tag -width indent -compact
+.It Pa /tmp
+default temporary file directory
+.It Pa ar.XXXXXX
+temporary file names
+.El
+.Sh COMPATIBILITY
+By default,
+.Nm 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
+.Nm ar
+is backward compatible with previous versions of
+.Nm ar
+in that it can read and write (using the
+.Fl T
+option) historic archives.
+The
+.Fl T
+option is provided for compatibility only, and will be deleted
+in a future release.
+See
+.Xr ar 5
+for more information.
+.Sh STANDARDS
+The
+.Nm ar
+utility is expected to offer a superset of the
+.St -p1003.2
+functionality.
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr ranlib 1 ,
+.Xr strmode 3 ,
+.Xr ar 5
diff --git a/usr.bin/ar/ar.1aout b/usr.bin/ar/ar.1aout
new file mode 100644
index 0000000..e9118e7
--- /dev/null
+++ b/usr.bin/ar/ar.1aout
@@ -0,0 +1,318 @@
+.\" 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
+.\" $Id: ar.1,v 1.4 1997/02/22 19:54:01 peter Exp $
+.\"
+.Dd June 29, 1993
+.Dt AR 1
+.Sh NAME
+.Nm ar
+.Nd create and maintain library archives
+.Sh SYNOPSIS
+.Nm ar
+.Fl d
+.Op Fl \Tv
+.Ar archive file ...
+.Nm ar
+.Fl m
+.Op Fl \Tv
+.Ar archive file ...
+.Nm ar
+.Fl m
+.Op Fl abiTv
+.Ar position archive file ...
+.Nm ar
+.Fl p
+.Op Fl \Tv
+.Ar archive [file ...]
+.Nm ar
+.Fl q
+.Op Fl cTv
+.Ar archive file ...
+.Nm ar
+.Fl r
+.Op Fl cuTv
+.Ar archive file ...
+.Nm ar
+.Fl r
+.Op Fl abciuTv
+.Ar position archive file ...
+.Nm ar
+.Fl t
+.Op Fl \Tv
+.Ar archive [file ...]
+.Nm ar
+.Fl x
+.Op Fl ouTv
+.Ar archive [file ...]
+.Sh DESCRIPTION
+The
+.Nm 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
+.Em first
+file with a matching name will be selected.
+.Pp
+The normal use of
+.Nm ar
+is for the creation and maintenance of libraries suitable for use with
+the loader (see
+.Xr ld 1 )
+although it is not restricted to this purpose.
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+A positioning modifier used with the options
+.Fl r
+and
+.Fl m .
+The files are entered or moved
+.Em after
+the archive member
+.Ar position ,
+which must be specified.
+.It Fl b
+A positioning modifier used with the options
+.Fl r
+and
+.Fl m .
+The files are entered or moved
+.Em before
+the archive member
+.Ar position ,
+which must be specified.
+.It Fl c
+Whenever an archive is created, an informational message to that effect
+is written to standard error.
+If the
+.Fl c
+option is specified,
+.Nm ar
+creates the archive silently.
+.It Fl d
+Delete the specified archive files.
+.It Fl i
+Identical to the
+.Fl b
+option.
+.It Fl m
+Move the specified archive files within the archive.
+If one of the options
+.Fl a ,
+.Fl b
+or
+.Fl i
+is specified, the files are moved
+before or after the
+.Ar position
+file in the archive.
+If none of those options are specified, the files are moved
+to the end of the archive.
+.It Fl 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.
+.It Fl 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.
+.It Fl 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
+.Fl 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.
+.It Fl 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
+.Fl a ,
+.Fl b
+or
+.Fl i
+is specified.
+.It Fl 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
+.Xr ar 5
+for more information.)
+.It Fl 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.
+.It Fl u
+Update files.
+When used with the
+.Fl 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
+.Fl x
+option, files in the archive will be extracted
+only if the archive file has a newer modification time than the file
+on disk.
+.It Fl v
+Provide verbose output.
+When used with the
+.Fl d ,
+.Fl m ,
+.Fl q
+or
+.Fl x
+options,
+.Nm 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
+.Fl r
+option,
+.Nm 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.
+.Pp
+When used with the
+.Fl 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.
+.Pp
+When used with the
+.Fl t
+option,
+.Nm 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
+.Xr 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
+.Xr date 1
+format ``%b %e %H:%M %Y''), and the name of the file.
+.It Fl 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.
+.Pp
+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
+.Fl 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.
+.El
+.Pp
+The
+.Nm ar
+utility exits 0 on success, and >0 if an error occurs.
+.Sh ENVIRONMENT
+.Bl -tag -width indent -compact
+.It Ev TMPDIR
+The pathname of the directory to use when creating temporary files.
+.El
+.Sh FILES
+.Bl -tag -width indent -compact
+.It Pa /tmp
+default temporary file directory
+.It Pa ar.XXXXXX
+temporary file names
+.El
+.Sh COMPATIBILITY
+By default,
+.Nm 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
+.Nm ar
+is backward compatible with previous versions of
+.Nm ar
+in that it can read and write (using the
+.Fl T
+option) historic archives.
+The
+.Fl T
+option is provided for compatibility only, and will be deleted
+in a future release.
+See
+.Xr ar 5
+for more information.
+.Sh STANDARDS
+The
+.Nm ar
+utility is expected to offer a superset of the
+.St -p1003.2
+functionality.
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr ranlib 1 ,
+.Xr strmode 3 ,
+.Xr ar 5
diff --git a/usr.bin/ar/ar.5 b/usr.bin/ar/ar.5
new file mode 100644
index 0000000..b5d3258
--- /dev/null
+++ b/usr.bin/ar/ar.5
@@ -0,0 +1,146 @@
+.\" 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
+.Bx 4.4 .
+.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..2ab5b2d
--- /dev/null
+++ b/usr.bin/ar/ar.5.5
@@ -0,0 +1,146 @@
+.\" 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.2 (Berkeley) 6/1/94
+.\"
+.Dd June 1, 1994
+.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 .
+.ne 1i
+.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..f27ff84
--- /dev/null
+++ b/usr.bin/ar/ar.c
@@ -0,0 +1,243 @@
+/*-
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 <locale.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 **));
+
+ (void) setlocale(LC_TIME, "");;
+
+ 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")) != -1) {
+ 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();
+ }
+
+ /* -dmr require a list of archive elements. */
+ if (options & (AR_D|AR_M|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, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: 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 ...]");
+ exit(1);
+}
diff --git a/usr.bin/ar/archive.c b/usr.bin/ar/archive.c
new file mode 100644
index 0000000..8ab1111
--- /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.tv_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.tv_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.tv_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..bcd9711
--- /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;
+{
+
+ err(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..b9a6cc6
--- /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..6e1581c
--- /dev/null
+++ b/usr.bin/at/Makefile
@@ -0,0 +1,33 @@
+# $Id$
+
+.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
+ @${ECHO} Making ${.TARGET:T} from ${.ALLSRC:T}; \
+ 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.c b/usr.bin/at/at.c
new file mode 100644
index 0000000..2dea501
--- /dev/null
+++ b/usr.bin/at/at.c
@@ -0,0 +1,773 @@
+/*
+ * 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 <sys/param.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>
+#include <utmp.h>
+#ifndef __FreeBSD__
+#include <getopt.h>
+#else
+#include <locale.h>
+#endif
+
+#if (MAXLOGNAME-1) > UT_NAMESIZE
+#define LOGNAMESIZE UT_NAMESIZE
+#else
+#define LOGNAMESIZE (MAXLOGNAME-1)
+#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.12 1997/03/03 07:58:00 ache 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;
+
+#ifdef __FreeBSD__
+ (void) setlocale(LC_TIME, "");
+#endif
+
+/* 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) > LOGNAMESIZE) || (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 %*s %d\n",
+ (long) real_uid, (long) real_gid, LOGNAMESIZE, 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)) != -1)
+ 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..daffd0a
--- /dev/null
+++ b/usr.bin/at/at.man
@@ -0,0 +1,268 @@
+.\" $Id$
+.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..7ef0e15d
--- /dev/null
+++ b/usr.bin/at/panic.c
@@ -0,0 +1,80 @@
+/*
+ * 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.6 1997/02/22 19:54:06 peter 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..6e64f1f
--- /dev/null
+++ b/usr.bin/at/parsetime.c
@@ -0,0 +1,621 @@
+/*
+ * 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 <err.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.10 1997/06/23 06:44:18 charnier 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)
+ warnx("pluralization is wrong");
+ 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) {
+ if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */
+ hour += 12;
+ } else {
+ if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */
+ hour = 0;
+ }
+ 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/perm.c b/usr.bin/at/perm.c
new file mode 100644
index 0000000..d52fd3d
--- /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$";
+
+/* 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..eb6414f
--- /dev/null
+++ b/usr.bin/banner/banner.6
@@ -0,0 +1,68 @@
+.\" Copyright (c) 1980, 1993, 1995
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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.2 (Berkeley) 4/29/95
+.\"
+.Dd "April 29, 1995"
+.Dt BANNER 6
+.Os
+.Sh NAME
+.Nm banner
+.Nd print large banner on printer
+.Sh SYNOPSIS
+.Nm banner
+.Op Fl w Ar n
+.Ar message ...
+.Sh DESCRIPTION
+.Nm 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
+.Fl w
+is given, the output is changed from a width of 132 to
+.Ar n ,
+suitable for a narrow terminal.
+.Pp
+The output should be printed on paper of the appropriate width,
+with no breaks between the pages.
+.Sh BUGS
+Several ASCII characters are not defined, notably <, >, [, ], \\,
+^, _, {, }, |, and ~.
+Also, the characters ", ', and & are funny looking (but in a useful way.)
+.Pp
+The
+.Fl 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..13a2849
--- /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.4 (Berkeley) 4/29/95";
+#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")) != -1)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 't':
+ trace = 1;
+ break;
+ case 'w':
+ width = atoi(optarg);
+ if (width <= 0)
+ errx(1, "illegal argument for -w option");
+ break;
+ case '?':
+ default:
+ (void)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..796bb6b
--- /dev/null
+++ b/usr.bin/basename/basename.1
@@ -0,0 +1,98 @@
+.\" 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
+.\" $Id$
+.\"
+.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..5964013
--- /dev/null
+++ b/usr.bin/basename/basename.c
@@ -0,0 +1,139 @@
+/*-
+ * 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.4 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *p;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ 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..c0caf75
--- /dev/null
+++ b/usr.bin/biff/biff.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.
+.\"
+.\" @(#)biff.1 8.1 (Berkeley) 6/6/93
+.\" $Id: biff.1,v 1.4 1997/02/22 19:54:13 peter Exp $
+.\"
+.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 n | y
+.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 .
+It was name after the dog of Heidi Stettner. He died
+in August 1993, at 15.
diff --git a/usr.bin/biff/biff.c b/usr.bin/biff/biff.c
new file mode 100644
index 0000000..a3e450c
--- /dev/null
+++ b/usr.bin/biff/biff.c
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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>
+#include <err.h>
+
+static void usage __P((void));
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct stat sb;
+ int ch;
+ char *name;
+
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ((name = ttyname(STDERR_FILENO)) == NULL)
+ err(2, "unknown tty");
+
+ if (stat(name, &sb))
+ err(2, "stat");
+
+ 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(2, name);
+ break;
+ case 'y':
+ if (chmod(name, sb.st_mode | 0100) < 0)
+ err(2, name);
+ break;
+ default:
+ usage();
+ }
+ exit(sb.st_mode & 0100 ? 0 : 1);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: biff [y | n]\n");
+ exit(2);
+}
diff --git a/usr.bin/brandelf/Makefile b/usr.bin/brandelf/Makefile
new file mode 100644
index 0000000..3561ec0
--- /dev/null
+++ b/usr.bin/brandelf/Makefile
@@ -0,0 +1,3 @@
+PROG= brandelf
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/brandelf/brandelf.1 b/usr.bin/brandelf/brandelf.1
new file mode 100644
index 0000000..3ebfe27
--- /dev/null
+++ b/usr.bin/brandelf/brandelf.1
@@ -0,0 +1,84 @@
+.\" Copyright (c) 1997
+.\" John-Mark Gurney. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the 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 John-Mark Gurney AND CONTRIBUTORS ``AS IS''
+.\" AND ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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 February 6, 1997
+.Dt BRANDELF 1
+.Os FreeBSD
+.Sh NAME
+.Nm brandelf
+.Nd mark an ELF binary for a specific ABI
+.Sh SYNOPSIS
+.Nm brandelf
+.Op Fl v
+.Op Fl t Ar string
+.Ar file ...
+.Sh DESCRIPTION
+This command marks an ELF binary to be run under a certain ABI for
+.Tn FreeBSD .
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl v
+turns on verbose reporting
+.It Fl t Ar string
+Brands the given ELF binaries with
+.Ar string
+as the ABI type. Currently supported ABI's are
+.Dq Tn FreeBSD
+and
+.Dq Linux .
+.It Ar file
+If
+.Fl t Ar string
+is given it will brand
+.Ar file
+with
+.Ar string ,
+otherwise it will simply display the branding of
+.Ar file .
+.El
+.Sh EXAMPLES
+The following is an example of a typical usage
+of the
+.Nm
+command:
+.Pp
+.Dl % brandelf file
+.Dl % brandelf -t Linux file
+.Sh DIAGNOSTICS
+Exit status is 0 on success, and 1 if the command
+fails if a file doesn't exist, is too short, or fails to brand properly.
+.Sh HISTORY
+The
+.Nm
+manual page first appeared in
+.Fx 2.2 .
+.Sh AUTHOR
+This
+manual page was written by John-Mark Gurney
+.Aq gurney_j@efn.org .
diff --git a/usr.bin/brandelf/brandelf.c b/usr.bin/brandelf/brandelf.c
new file mode 100644
index 0000000..86519aa
--- /dev/null
+++ b/usr.bin/brandelf/brandelf.c
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (c) 1996 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce 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 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: brandelf.c,v 1.6 1997/05/21 23:07:17 jdp Exp $
+ */
+
+#include <elf.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+
+ const char *type = "FreeBSD";
+ int retval = 0;
+ int ch, change = 0, verbose = 0;
+
+ while ((ch = getopt(argc, argv, "t:v")) != -1)
+ switch (ch) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 't':
+ change = 1;
+ type = optarg;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (!argc)
+ errx(1, "no file(s) specified");
+ while (argc) {
+ int fd;
+ char buffer[EI_NIDENT];
+ char string[(EI_NIDENT-EI_BRAND)+1];
+
+ if ((fd = open(argv[0], O_RDWR, 0)) < 0) {
+ warnx("no such file %s", argv[0]);
+ retval = 1;
+ goto fail;
+
+ }
+ if (read(fd, buffer, EI_NIDENT) < EI_NIDENT) {
+ warnx("file '%s' too short", argv[0]);
+ retval = 1;
+ goto fail;
+ }
+ if (buffer[0] != ELFMAG0 || buffer[1] != ELFMAG1 ||
+ buffer[2] != ELFMAG2 || buffer[3] != ELFMAG3) {
+ warnx("file '%s' is not ELF format", argv[0]);
+ retval = 1;
+ goto fail;
+ }
+ if (!change) {
+ bzero(string, sizeof(string));
+ strncpy(string, &buffer[EI_BRAND], EI_NIDENT-EI_BRAND);
+ if (strlen(string)) {
+ fprintf(stdout, "File '%s' is of brand '%s'.\n",
+ argv[0], string);
+ }
+ else
+ fprintf(stdout, "File '%s' has no branding.\n",
+ argv[0]);
+ }
+ else {
+ strncpy(&buffer[EI_BRAND], type, EI_NIDENT-EI_BRAND);
+ lseek(fd, 0, SEEK_SET);
+ if (write(fd, buffer, EI_NIDENT) != EI_NIDENT) {
+ warnx("error writing %s", argv[0]);
+ retval = 1;
+ goto fail;
+ }
+ }
+fail:
+ argc--;
+ argv++;
+ }
+
+ return retval;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: brandelf [-t string] file ...\n");
+ exit(1);
+}
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..7fff6cb
--- /dev/null
+++ b/usr.bin/cal/cal.1
@@ -0,0 +1,82 @@
+.\" 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.2 (Berkeley) 4/28/95
+.\"
+.Dd April 28, 1995
+.Dt CAL 1
+.Os
+.Sh NAME
+.Nm cal
+.Nd displays a calendar
+.Sh SYNOPSIS
+.Nm cal
+.Op Fl jy
+.Op Oo Ar month Oc 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
+.At v6 .
diff --git a/usr.bin/cal/cal.c b/usr.bin/cal/cal.c
new file mode 100644
index 0000000..fbd58ee
--- /dev/null
+++ b/usr.bin/cal/cal.c
@@ -0,0 +1,441 @@
+/*
+ * 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 <locale.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];
+
+char day_headings[] = " ";
+char j_day_headings[] = " ";
+
+/* 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;
+ static struct tm zero_tm;
+ time_t now;
+ int ch, month, year, yflag, i;
+ char buf[40];
+
+ (void) setlocale(LC_TIME, "");
+
+ yflag = 0;
+ while ((ch = getopt(argc, argv, "jy")) != -1)
+ 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();
+ }
+
+ for (i = 0; i < 12; i++) {
+ zero_tm.tm_mon = i;
+ strftime(buf, sizeof(buf), "%B", &zero_tm);
+ month_names[i] = strdup(buf);
+ }
+ for (i = 0; i < 7; i++) {
+ zero_tm.tm_wday = i;
+ strftime(buf, sizeof(buf), "%a", &zero_tm);
+ strncpy(day_headings + i * 3, buf, 2);
+ strncpy(j_day_headings + i * 4 + 1, buf, 2);
+ }
+
+ 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..ad577f4
--- /dev/null
+++ b/usr.bin/calendar/Makefile
@@ -0,0 +1,18 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= calendar
+SRCS= calendar.c io.c day.c ostern.c paskha.c
+CFLAGS+= -Wall
+INTER= de_DE.ISO_8859-1 hr_HR.ISO_8859-2 ru_SU.KOI8-R
+TEXTMODE?= 444
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${TEXTMODE} \
+ ${.CURDIR}/calendars/calendar.* ${DESTDIR}${SHAREDIR}/calendar
+.for lang in ${INTER}
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${TEXTMODE} \
+ ${.CURDIR}/calendars/${lang}/calendar.* \
+ ${DESTDIR}${SHAREDIR}/calendar/${lang};
+.endfor
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/calendar/calendar.1 b/usr.bin/calendar/calendar.1
new file mode 100644
index 0000000..b8d13a6
--- /dev/null
+++ b/usr.bin/calendar/calendar.1
@@ -0,0 +1,230 @@
+.\" 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
+.Op Fl A Ar num
+.Op Fl B Ar num
+.Oo Fl t Ar dd
+.Sm off
+.Op . Ar mm Op . Ar year
+.Sm on
+.Oc
+.Op Fl f Ar calendarfile
+.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.
+.It Fl A Ar num
+Print lines from today and the next
+.Ar num
+days (forward, future).
+.It Fl B Ar num
+Print lines from today and the previous
+.Ar num
+days (backward, past).
+.It Fl f Pa calendarfile
+Use
+.Pa calendarfile
+as the default calendar file.
+.It Xo Fl t
+.Sm off
+.Ar dd
+.Op . Ar mm Op . Ar year
+.Sm on
+.Xc
+For test purposes only: set date directly to argument values.
+.El
+.Pp
+To handle calendars in your national code table you can specify
+.Dq LANG=<locale_name>
+in the calendar file as early as possible. To handle national Easter
+names in the calendars
+.Dq Easter=<national_name>
+(for Catholic Easter) or
+.Dq Paskha=<national_name>
+(for Orthodox Easter) can be used.
+.Pp
+Other lines should begin with a month and day.
+They may be entered in almost any format, either numeric or as character
+strings.
+If the proper locale is set, national month and weekday
+names can be used.
+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.
+.Pp
+``Easter'', is Easter for this year, and may be followed by a positive
+or negative integer.
+.Pp
+``Paskha'', is Orthodox Easter for this year, and may be followed by a
+positive or negative integer.
+.Pp
+Weekdays may be followed by ``-4'' ... ``+5'' (aliases for
+last, first, second, third, fourth) for moving events like
+``the last Monday in April''
+.Pp
+By convention, dates followed by an asterisk are not fixed, i.e., change
+from year to year.
+.Pp
+Day descriptions start after the first <tab> character in the line;
+if the line does not contain a <tab> character, it is not displayed.
+If the first character in the line is a <tab> character, it is treated as
+a continuation of the previous line.
+.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 (<tab> characters highlighted by
+\fB\et\fR sequence)
+.Bd -unfilled -offset indent
+LANG=C
+Easter=Ostern
+
+#include <calendar.usholiday>
+#include <calendar.birthday>
+
+6/15\fB\et\fRJune 15 (if ambiguous, will default to month/day).
+Jun. 15\fB\et\fRJune 15.
+15 June\fB\et\fRJune 15.
+Thursday\fB\et\fREvery Thursday.
+June\fB\et\fREvery June 1st.
+15 *\fB\et\fR15th of every month.
+
+May Sun+2\fB\et\fRsecond Sunday in May (Muttertag)
+04/SunLast\fB\et\fRlast Sunday in April,
+\fB\et\fRsummer time in Europe
+Easter\fB\et\fREaster
+Ostern-2\fB\et\fRGood Friday (2 days before Easter)
+Paskha\fB\et\fROrthodox Easter
+
+.Ed
+.Sh FILES
+.Pp
+.Bl -tag -width calendar.christian -compact
+.It Pa calendar
+file in current directory
+.It Pa ~/.calendar
+.Pa calendar
+HOME directory.
+.Nm calendar
+does a chdir into this directory if it exists.
+.It Pa ~/.calendar/calendar
+calendar file to use if no calendar file exists in the current directory.
+.It Pa ~/.calendar/nomail
+do not send mail if this file exists.
+.El
+.Pp
+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.
+.It Pa calendar.german
+German calendar.
+.It Pa calendar.russian
+Russian calendar.
+.El
+.Sh SEE ALSO
+.Xr at 1 ,
+.Xr cpp 1 ,
+.Xr mail 1 ,
+.Xr cron 8
+.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
+at the beginning of a line.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v7 .
+.Sh BUGS
+.Nm Calendar
+doesn't handle Jewish holidays and moon phases.
diff --git a/usr.bin/calendar/calendar.c b/usr.bin/calendar/calendar.c
new file mode 100644
index 0000000..64b5f6b
--- /dev/null
+++ b/usr.bin/calendar/calendar.c
@@ -0,0 +1,140 @@
+/*
+ * 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 const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94";
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+#include "calendar.h"
+
+struct passwd *pw;
+int doall = 0;
+time_t f_time = 0;
+
+int f_dayAfter = 0; /* days after current date */
+int f_dayBefore = 0; /* days before current date */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ int ch;
+
+ (void) setlocale(LC_ALL, "");
+
+ while ((ch = getopt(argc, argv, "?-af:t:A:B:")) != -1)
+ switch (ch) {
+ case '-': /* backward contemptible */
+ case 'a':
+ if (getuid()) {
+ errno = EPERM;
+ err(1, NULL);
+ }
+ doall = 1;
+ break;
+
+
+ case 'f': /* other calendar file */
+ calendarFile = optarg;
+ break;
+
+ case 't': /* other date, undocumented, for tests */
+ f_time = Mktime (optarg);
+ break;
+
+ case 'A': /* days after current date */
+ f_dayAfter = atoi(optarg);
+ break;
+
+ case 'B': /* days before current date */
+ f_dayBefore = atoi(optarg);
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc)
+ usage();
+
+ /* use current time */
+ if (f_time <= 0)
+ (void)time(&f_time);
+
+ settime(f_time);
+
+ if (doall)
+ while ((pw = getpwent()) != NULL) {
+ (void)setegid(pw->pw_gid);
+ (void)initgroups(pw->pw_name, pw->pw_gid);
+ (void)seteuid(pw->pw_uid);
+ if (!chdir(pw->pw_dir))
+ cal();
+ (void)seteuid(0);
+ }
+ else
+ cal();
+ exit(0);
+}
+
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: calendar [-a] [-A days] [-B days] [-f calendarfile]\n");
+ exit(1);
+}
+
+
diff --git a/usr.bin/calendar/calendar.h b/usr.bin/calendar/calendar.h
new file mode 100644
index 0000000..6579e11
--- /dev/null
+++ b/usr.bin/calendar/calendar.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+
+extern struct passwd *pw;
+extern int doall;
+extern struct iovec header[];
+extern struct tm *tp;
+extern char *calendarFile;
+extern char *optarg;
+
+void cal __P((void));
+void closecal __P((FILE *));
+int getday __P((char *));
+int getdayvar __P((char *));
+int getfield __P((char *, char **, int *));
+int getmonth __P((char *));
+int geteaster __P((char *, int));
+int getpaskha __P((char *, int));
+int easter __P((int));
+int isnow __P((char *, int *, int *, int *));
+FILE *opencal __P((void));
+void settime __P((time_t));
+time_t Mktime __P((char *));
+void usage __P((void));
+void setnnames __P((void));
+
+#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+
+/* some flags */
+#define F_ISMONTH 0x01 /* month (Januar ...) */
+#define F_ISDAY 0x02 /* day of week (Sun, Mon, ...) */
+#define F_ISDAYVAR 0x04 /* variables day of week, like SundayLast */
+#define F_EASTER 0x08 /* Easter or easter depending days */
+
+extern f_dayAfter; /* days after current date */
+extern f_dayBefore; /* days bevore current date */
+
+struct fixs {
+ char *name;
+ int len;
+};
+
diff --git a/usr.bin/calendar/calendars/calendar.all b/usr.bin/calendar/calendars/calendar.all
new file mode 100644
index 0000000..35a40cc
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.all
@@ -0,0 +1,16 @@
+/*
+ * International and national calendar files
+ *
+ * $Id$
+ */
+
+#ifndef _calendar_all_
+#define _calendar_all_
+
+#include <calendar.world>
+#include <calendar.german>
+#include <calendar.usholiday>
+#include <calendar.croatian>
+#include <calendar.russian>
+
+#endif /* !_calendar_all_ */
diff --git a/usr.bin/calendar/calendars/calendar.birthday b/usr.bin/calendar/calendars/calendar.birthday
new file mode 100644
index 0000000..99922af
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.birthday
@@ -0,0 +1,273 @@
+/*
+ * Birthday
+ *
+ * $Id: calendar.birthday,v 1.6 1997/02/25 01:20:23 mpp Exp $
+ */
+
+#ifndef _calendar_birthday_
+#define _calendar_birthday_
+
+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/15 J.J. Robert's Birthday in Liberia
+03/15 Julius Caesar assassinated by Brutus; Ides of March, 44BC
+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/26 David Packard died, 1996; age of 83
+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/07 Alan Mathison Turing died, 1954
+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/23 Alan Mathison Turing born, 1912
+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/04 John Adams and Thomas Jefferson die on same day, 1826
+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/12 Thoreau's Birthday, 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/12 Thomas Mann's Death, 1955
+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/20 Leon Trotsky assassinated, 1940
+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/19 Konrad Zuse died, 1995
+12/20 Carl Sagan died, 1996
+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
+
+#endif /* !_calendar_birthday_ */
diff --git a/usr.bin/calendar/calendars/calendar.christian b/usr.bin/calendar/calendars/calendar.christian
new file mode 100644
index 0000000..e238918
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.christian
@@ -0,0 +1,28 @@
+/*
+ * Christian
+ *
+ * $Id$
+ */
+
+#ifndef _calendar_christian_
+#define _calendar_christian_
+
+01/06* Epiphany
+Easter-47 Shrove Tuesday / Mardi Gras (day before Ash Wednesday)
+Easter-46 Ash Wednesday (First day of Lent)
+Easter-7 Palm Sunday (7 days before Easter)
+Easter-3 Maundy Thursday (3 days before Easter)
+Easter-2 Good Friday (2 days before Easter)
+Easter Easter Sunday
+Easter+39 Ascension Day (10 days before Pentecost)
+Easter+49 Pentecost (Whitsunday)
+Easter+50 Whitmonday
+Easter+56 Trinity Sunday (7 days after Pentecost)
+Easter+60 Corpus Christi (11 days after Pentecost)
+05/28* Rogation Sunday
+10/18 Feast Day of St. Luke
+11/SunLast First Sunday of Advent (4th Sunday before Christmas)
+12/SunFirst First Sunday of Advent (4th Sunday before Christmas)
+12/06 St. Nicholas' Day
+
+#endif /* !_calendar_christian_ */
diff --git a/usr.bin/calendar/calendars/calendar.computer b/usr.bin/calendar/calendars/calendar.computer
new file mode 100644
index 0000000..6c6ab17
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.computer
@@ -0,0 +1,73 @@
+/*
+ * Computer
+ *
+ * $Id$
+ */
+
+#ifndef _calendar_computer_
+#define _calendar_computer_
+
+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
+
+#endif /* !_calendar_computer_ */
diff --git a/usr.bin/calendar/calendars/calendar.croatian b/usr.bin/calendar/calendars/calendar.croatian
new file mode 100644
index 0000000..187a0c7
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.croatian
@@ -0,0 +1,12 @@
+/*
+ * Croatian calendar files
+ *
+ * $Id$
+ */
+
+#ifndef _calendar_croatian_
+#define _calendar_croatian_
+
+#include <hr_HR.ISO_8859-2/calendar.all>
+
+#endif /* !_calendar_croatian_ */
diff --git a/usr.bin/calendar/calendars/calendar.german b/usr.bin/calendar/calendars/calendar.german
new file mode 100644
index 0000000..7ee93eb
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.german
@@ -0,0 +1,12 @@
+/*
+ * German calendar file(s)
+ *
+ * $Id$
+ */
+
+#ifndef _calendar_german_
+#define _calendar_german_
+
+#include <de_DE.ISO_8859-1/calendar.all>
+
+#endif /* !_calendar_german_ */
diff --git a/usr.bin/calendar/calendars/calendar.history b/usr.bin/calendar/calendars/calendar.history
new file mode 100644
index 0000000..961f429
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.history
@@ -0,0 +1,499 @@
+/*
+ * History
+ *
+ * $Id: calendar.history,v 1.6 1997/02/22 19:28:21 peter Exp $
+ */
+
+#ifndef _calendar_history_
+#define _calendar_history_
+
+01/01 Anniversary of the Triumph of the Revolution in Cuba
+01/01 Castro expels 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, 1788
+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 kidnaped 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 Day of the 1848 revolution in Hungary
+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 baptized in Stratford-on-Avon, England, 1564,
+ birthdate unknown
+04/27 Magellan killed in Philippines, 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 assassinated, 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 Plymouth 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 subpoenas President Nixon, 1974
+09/22 The first Soviet atomic bomb explodes, 1949
+09/23 Philippine 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 announce 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 Day of the 1956 revolution in Hungary
+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 separate 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 space dog 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 demonstrations 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
+
+#endif /* !_calendar_history_ */
diff --git a/usr.bin/calendar/calendars/calendar.holiday b/usr.bin/calendar/calendars/calendar.holiday
new file mode 100644
index 0000000..6da2a1a
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.holiday
@@ -0,0 +1,579 @@
+/*
+ * Holiday
+ *
+ * $Id: calendar.holiday,v 1.6 1997/02/22 19:28:22 peter Exp $
+ */
+
+#ifndef _calendar_holiday_
+#define _calendar_holiday_
+
+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/SunThird Martin Luther King Day in New York (3rd Sunday)
+01/MonThird Robert E. Lee's Birthday in Alabama & Mississippi (3rd Monday)
+01/MonThird Lee-Jackson Day in Virginia (3rd Monday)
+01/21 Our Lady of Altagracia in Dominican Republic
+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/MonLast 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/MonThird 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/MonLast Arbor Day in Wyoming (last Monday)
+04/MonLast 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 Guatemala
+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/MonThird 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/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 Botswana
+07/17 Constitution Day in South Korea
+07/17 Public Holiday in Botswana
+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 Egyptian 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/FriThird 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 Massachusetts
+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 Democratic Republic
+09/26 Revolution Anniversary Day in Yemen Arab
+09/28 Confucius' Day in Taiwan
+09/30 Botswana Day in Botswana
+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 Botswana
+10/03 National Foundation Day in South Korea
+10/03 U.N. Day in Barbados
+10/03 German Reunification Day in Germany
+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 Republic
+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 Thailand
+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 Remembrance 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 Democratic 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/01 World AIDS Day
+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 Victory 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 Democratic 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/MonFirst Jefferson Davis's Birthday in Alabama & Mississippi (1st Monday)
+06/MonFirst 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 Anniversary 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
+
+#endif /* !_calendar_holiday_ */
diff --git a/usr.bin/calendar/calendars/calendar.judaic b/usr.bin/calendar/calendars/calendar.judaic
new file mode 100644
index 0000000..f162872
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.judaic
@@ -0,0 +1,41 @@
+/*
+ * Judaic
+ *
+ * $Id$
+ */
+
+#ifndef _calendar_judaic_
+#define _calendar_judaic_
+
+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)
+
+#endif /* !_calendar_judaic_ */
diff --git a/usr.bin/calendar/calendars/calendar.music b/usr.bin/calendar/calendars/calendar.music
new file mode 100644
index 0000000..5a60e59
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.music
@@ -0,0 +1,190 @@
+/*
+ * Music
+ *
+ * $Id: calendar.music,v 1.6 1997/02/22 19:28:23 peter Exp $
+ */
+
+#ifndef _calendar_music_
+#define _calendar_music_
+
+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 Elvis Presley born, 1935
+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 Philadelphia, 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 Pavilion, 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
+
+#endif /* !_calendar_music_ */
diff --git a/usr.bin/calendar/calendars/calendar.russian b/usr.bin/calendar/calendars/calendar.russian
new file mode 100644
index 0000000..ce192a0
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.russian
@@ -0,0 +1,12 @@
+/*
+ * Russian calendar files
+ *
+ * $Id$
+ */
+
+#ifndef _calendar_russian_
+#define _calendar_russian_
+
+#include <ru_SU.KOI8-R/calendar.all>
+
+#endif /* !_calendar_russian_ */
diff --git a/usr.bin/calendar/calendars/calendar.usholiday b/usr.bin/calendar/calendars/calendar.usholiday
new file mode 100644
index 0000000..1d3e45d
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.usholiday
@@ -0,0 +1,42 @@
+/*
+ * USA holiday
+ *
+ * $Id$
+ */
+
+#ifndef _calendar_usholiday_
+#define _calendar_usholiday_
+
+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/MonThird 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/SunFirst Daylight Savings Time begins; clocks move forward (1st Sunday of April)
+04/28* Arbor Day (varies from state to state)
+05/SunSecond Mother's Day (2nd Sunday of May)
+05/SatThird Armed Forces Day (3rd Saturday of May)
+05/MonLast Memorial Day (Last Monday of May)
+06/SunThird Father's Day (3rd Sunday of June)
+06/21* Summer Solstice
+07/04 Independence Day
+09/MonFirst Labor Day (1st Monday of September)
+09/SunSecond Grandparent's Day (2nd Sunday of September; varies from state to state)
+09/22* Autumnal Equinox
+10/MonSecond Columbus Day (2nd Monday of October)
+10/SunLast Daylight Savings Time ends; clocks move back (Last Sunday in October)
+10/31 All Hallows Eve (Halloween)
+11/05* Election Day (1st Tuesday after 1st Monday for even years)
+11/11 Veterans' Day
+11/ThuFourth Thanksgiving Day (4th Thursday in November)
+12/21* Winter Solstice
+12/24 Christmas Eve
+12/25 Christmas
+12/31 New Year's Eve
+
+#endif /* !_calendar_usholiday_ */
diff --git a/usr.bin/calendar/calendars/calendar.world b/usr.bin/calendar/calendars/calendar.world
new file mode 100644
index 0000000..8720139
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.world
@@ -0,0 +1,18 @@
+/*
+ * World wide calendar files, except national calendars
+ *
+ * $Id$
+ */
+
+#ifndef _calendar_world_
+#define _calendar_world_
+
+#include <calendar.birthday>
+#include <calendar.christian>
+#include <calendar.computer>
+#include <calendar.history>
+#include <calendar.holiday>
+#include <calendar.judaic>
+#include <calendar.music>
+
+#endif /* !_calendar_world_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.all b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.all
new file mode 100644
index 0000000..72068aa
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.all
@@ -0,0 +1,17 @@
+/*
+ * deutscher Kalender
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_all_
+#define _de_DE_ISO_8859_1_all_
+
+#include <de_DE.ISO_8859-1/calendar.feiertag>
+#include <de_DE.ISO_8859-1/calendar.geschichte>
+#include <de_DE.ISO_8859-1/calendar.kirche>
+#include <de_DE.ISO_8859-1/calendar.literatur>
+#include <de_DE.ISO_8859-1/calendar.musik>
+#include <de_DE.ISO_8859-1/calendar.wissenschaft>
+
+#endif /* !_de_DE.ISO_8859-1_all_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.feiertag b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.feiertag
new file mode 100644
index 0000000..d723afa
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.feiertag
@@ -0,0 +1,42 @@
+/*
+ * Feiertage
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_feiertag_
+#define _de_DE_ISO_8859_1_feiertag_
+
+LANG=de_DE.ISO_8859-1
+
+/* arbeitsfreie staatliche Feiertage */
+01/01 Neujahr
+05/01 Maifeiertag
+10/03 Tag der deutschen Einheit
+
+/* arbeitsfreie christliche Feiertage */
+Easter-2 Karfreitag
+Easter Ostersonntag
+Easter+1 Ostermontag
+Easter+49 Pfingstsonntag
+Easter+50 Pfingstmontag
+Easter+39 Christi Himmelfahrt
+
+/* Gedenktage - nicht arbeitsfreie Feiertage :-( */
+06/17 Arbeiteraufstand am 17. Juni 1953
+/* ??/?? Befreiung des KZs Auschwitz */
+
+/* Jahreszeiten */
+03/21* Frühlingsanfang
+06/21* Sommeranfang
+09/21* Herbstanfang
+12/21* Winteranfang
+
+/* Sommer- und Winterzeit */
+03/SundayLast Anfang der Sommerzeit
+10/SundayLast Ende der Sommerzeit
+
+/* Blumenverkäufer */
+May Sun+2 Muttertag
+
+#endif /*! _de_DE_ISO_8859_1_feiertag_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.geschichte b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.geschichte
new file mode 100644
index 0000000..6a2a25e
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.geschichte
@@ -0,0 +1,197 @@
+/*
+ * deutsche Geschichte
+ *
+ *
+ * Die Angaben wurden überwiegend entnommen aus dem Buch:
+ *
+ * Fragen an die deutsch Geschichte, Ideen, Kräfte, Entscheidungen von
+ * 1800 bis zur Gegenwart; historische Ausstellung im Reichstagsgebäude
+ * in Berlin; Katalog, 16. Auflage, Sonderausgabe - Bonn: Deutscher
+ * Bundestag, Referat Öffentlichkeitsarbeit, 1990
+ *
+ * English Title: Question on German history
+ *
+ * ISBN 3-924521-59-X
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_geschichte_
+#define _de_DE_ISO_8859_1_geschichte_
+
+LANG=de_DE.ISO_8859-1
+
+/* 1800-1933 */
+07/11 Gründung der Rheinbundes, 1806
+10/14 Doppelschlacht bei Jena und Auerstedt, 1806
+10/16 Völkerschlacht bei Leipzig, 1813
+06/18 Niederlage Napoleons bei Waterloo, 1815
+10/18 Wartburgfest der Deutschen Burschenschaften, 1817
+01/01 Inkrafttreten des Vertrages über den deutschen. Zollverein, 1834
+12/07 erste deutsche Eisenbahn zwischen Nürnberg und Fürth, 1835
+06 Aufstand der schlesischen Weber, 1844
+12/21 Verabschiedung des Gesetzes über die Grundrechte des deutschen
+ Volkes durch die Frankfurter Nationalversammlung, 1848
+03/27 Annahme der deutschen Reichsverfassung in der Frankfurter
+ Paulskirche, Wahl von Friedrich Wilhelm IV von Preußen zum
+ deutschen Kaiser, 1849
+04/28 Ablehnung der deutschen Kaiserkrone durch den preußichen König, 1849
+07/03 Schlacht von Königgrätz
+07/13 Emser Depesche, 1870
+07/18 Verkündung des Dogmas von der päpstlichen Unfehlbarkeit
+ durch das I. Vatikanische Konzil
+01/18 Proklamation des deutschen Kaiserreiches in Versailles, 1871
+10/18 Verabschiedung des Sozialistengesetzes durch den Reichstag, 1878
+03/20 Entlassung von Bismarck als Reichskanzler und
+ preußischer Ministerpräsident, 1890
+06/21 Eröffnung des Nord-Ostsee-Kanals, 1895
+01/07 Billigung des Bürgerlichen Gesetzbuches (BGB) durch den Reichstag, 1896
+01/01 Bürgerliches Gesetzbuches tritt in Kraft, 1900
+06/28 Ermordung des österreichischen Thronfolgers Erzherzog Franz
+ Ferdinand durch serbische Nationalisten in Sarajewo, 1914
+07/28 Kriegserklärung Österreich-Ungarns an Serbien, 1914
+08/01 Deutsche Mobilmachung und Kriegserklärung an Rußland, 1914
+08/03 Deutsche Kriegserklärung an Frankreich, 1914
+08/04 Kriegserklärung Großbritaniens an Deutschland, 1914
+08/04 Bewilligung der Kriegskredite im Reichstag, 1914
+08/26 Schlacht bei Tannenberg, 1914
+02/21 Schlacht um Verdun, 1916
+03/08 Ausbruch der Revolution in Rußland, Abdankung von Zar Nikolaus II, 1917
+04/06 Kriegserklärung der USA an Deutschland
+12/15 Waffenstillstand zwisch Rußland und Deutschland, 1917
+03/03 Frieden von Brest-Litowsk, 1918
+11/03 Matrosenaufstand in Kiel, 1918
+11/09 Ausrufung der Republik durch Scheidemann (SPD), 1918
+02/11 Friedrich Ebert wird Reichspräsident, Weimar 1919
+06/28 Unterzeichnung des Versailler Vertrages, 1919
+03/21 Volksabstimmung in Oberschlesien, 1921
+04/16 Vertrag von Rapallo, 1922
+06/24 Ermordung von Reichsaußenminister Rathenau, 1922
+01/11 Besetzung des Ruhrgebietes durch Frankreich+Belgien, 1923
+09/10 Eintritt Deutschlands in den Völkerbund, 1926
+10/25 Schwarzer Freitag in New York, Beginn der Weltwirtschaftskrise, 1929
+
+
+/* II. Weltkrieg */
+10/14 Austritt auf dem Völkerbund, 1933
+03/16 Wiedereinführung der allgemeinen Wehrpflicht, 1935
+10/25 Deutsch-italienischer Vertrag, Achse Berlin-Rom, 1936
+11/25 Antikominternpakt zwischen Deutschland und Japan, 1936
+01/13 Volksabstimmung im Saargebiet über die Rückführung ins Reich, 1935
+03/12 Einmarsch deutscher Truppen in Österreich, 1938
+09/29 Münchner Abkommen, 1938
+03/15 Einmarsch deutscher Truppen in die Tschechoslowakei, 1939
+03/23 Rückgabe des Memelgebietes an Deutschland, 1939
+08/23 Abschluß des Hitler-Stalin-Paktes, 1939
+09/03 Kriegserklärung Großbritaniens und Frankreichs an Deutschland, 1939
+04/09 Besetzung Dänemarks, Invasion in Norwegen, 1940
+05/10 Deutscher Angriff auf Belgien, die Niederlande, Luxemburg
+ und Frankreich, 1940
+06/22 Angriff gegen die Sowjetunion, 1941
+12/11 Kriegserklärung Deutschlands an die USA, 1941
+01/14 Konferenz von Casablanca, 1943
+01/31 Kapitulation der 6. deutschen Armee in Stalingrad, 1943
+06/06 Alliierte Landung in Nordwestfrankreich, 1944
+02/04 Konferenz von Jalta, 4.-11.2. 1945
+04/25 Zusammentreffen von amerikanischen und sowjetischer Truppen
+ bei Torgau an der Elbe, 1945
+05/08 Bedingungslose Kapitulation von Deutschland, 1945
+07/01 Rückzug der britischen und amerikanischen Truppen aus
+ Sachsen, Thüringen und Mecklenburg, Einmarsch westlicher
+ Truppen in Berlin, 1945
+07/17 Potsdammer Konferenz, 1945
+09/01 Überfall auf Polen, Beginn des 2. Weltkrieges, 1939
+10/01 Verkündigung der Urteile im Nürnberger
+ Hauptkriegsverbrecherprozeß, 1946
+02/25 Auflösung der Landes Preußen durch den Kontrollrat, 1947
+08/06 erster Atombombenabwurf auf Hiroshima, 1945
+08/08 Atombombenabwurf auf Nagasaki, 1945
+04/19 Warschauer Ghetto Aufstand, 1943
+12/07 Japan bombardiert Pearl Harbor, 1941
+
+/* Deutschland nach dem 2. Weltkrieg */
+04/11 Attentat auf Dutschke, Studentenunruhen, 1958
+04/26 GAU in Tschernobyl
+05/05 Natobeitritt, Wiederbewaffnung, Souveränität der Bundesrepublik, 1955
+05/06 Rücktritt von Brandt, 1974
+05/16 Wahl von Schmidt (SPD) zum Bundeskanzler, 1974
+05/23 Verkündung des Grundgesetzes
+05/23 Wahl von Richard von Weizäcker zum Bundespräsidenten, 1984
+06/05 Marshallplan, 1947
+06/20 Währungsreform in den Westzonen, 1948
+06/24 Beginn der Berliner Blockade, 1948
+07/01 Wahl von Heinrich Lübke zum Bundespräsidenten, 1959
+07/01 Währungs- und Währungsunion, 1990
+08/12 deutsch-sowjetischer Gewaltverzichtsvertrag, Moskau 1970
+08/14 Wahl zum ersten deutschen Bundestag, 1949
+09/03 Vier-Mächte-Abkommen über Berlin, 1971
+09/05 Entführung und Ermordung von Arbeitgeberpräsident Schleyer, Entführung einer Lufthansa-Maschine nach Mogadischu, 1977
+09/07 DDR-Staatsratsvorsitzender Honecker in der Bundesrepublik, 1987
+09/12 Wahl von Theodor Heuss (FDP) zum Bundespräsidenten, 1949
+09/15 Wahl von Konrad Adenauer (CDU) zum Bundeskanzler, 1949
+09/17 Bruch der Sozialliberalen Koalition, 1982
+09/18 Aufnahme von Bundesrepublik und DDR in die UNO, 1993
+10/01 Ablösung von Bundeskanzler Schmidt durch Kohl, 1982
+10/23 Volksabstimmung im Saargebiet, 1955
+12/02 Washingtoner Abkommen über Bi-Zone, 1946
+12/07 deutsch-polnischer Vertrag, Warschau 1970
+12/10 Friedensnobelpreis für Brandt, 1971
+12/12 Nachrüstungsbeschluß des NATO-Ministerates, 1979
+12/21 Grundlagenvertrag zwischen DDR und Bundesrepublik, 1972
+
+
+/* Nationalsozialismus */
+11/09 Hitler-Putsch in München/Marsch auf die Feldherrenhalle, 1923
+11/09 Reichskristallnacht, 1938
+09/14 Reichstagswahl: Erdrutsch zugunsten der NSDAP, 1930
+07/31 Reichstagswahl: NSDAP wird stärkste Fraktion, 1932
+11/06 Reichstagswahl: Rückgang der NSDAP, 1932
+01/30 Ernennung von Hitler zum Reichskanzler, 1933
+02/27 Reichstagsbrand, 1933
+03/05 Reichstagswahl: Mehrheit für NSDAP+DNVP, 1933
+03/23 Annahme des Ermächtigungsgesetzes, 1933
+03/31 Erstes Gesetz zur Gleichschaltung der Länder, 1933
+04/01 Organisierter Boykott jüdischer Geschäfte, 1933
+04/07 Zweites Gesetz zur Gleichschaltung der Länder, 1933
+05/02 Auflösung der Gewerkschaften, 1933
+06 Auflösung aller Parteien außer NSDAP, 1933
+07/20 Konkordat zwischen Deutschland und dem Vatikan, 1933
+06/30 Röhm-Putsch, Ausschaltung der SA-Führung, 1934
+09/15 Nürnberger Gesetze, 1935
+01/08 Eröffnung der olympischen Spiele in Berlin, 1936
+01/20 Wannseekonferenz, 1942
+04/30 Selbstmord Hitlers, 1945
+07/29 Mussolini geboren, 1883
+
+/* Sozialismus */
+01/21 Lenin gestorben, 1924
+06 Gründung des Bundes der Kommunisten in London durch Marx+Engels, 1847
+05/23 Gründung des Allgemeinen Deutschen Arbeitervereins in Leipzig
+ unter Führung von Ferdinand Lassalles, 1863
+08/07 Gründung der Sozialdemokratischen Arbeiterpartei in Eisenach
+ unter der Führung von August Begel und Wilhelm Liebknecht, 1869
+04/06 Gründung der Unabhängigen Sozialdemokratischen Partei, Gotha 1917
+11/07 Oktoberrevolution in Rußland, Putsch der Bolschewisten, 1917
+12/31 Gründung der KPD, 1918
+01/15 Ermordung von Rosa Luxemburg und Karl Liebknecht, 1919
+03/05 Tod Stalins, 1953
+03/18 Erste demokratische Volkskammerwahl, 1990
+04/21 Zwangsvereinigung von KPD und SPD zur SED, 1946
+05/14 Gründung der Warschauer Paktes, 1955
+06/17 Arbeiteraufstand am 17. Juni 1953
+06/25 Begin der Korea-Krieges, 1950
+08/13 Bau der Berliner Mauer, 1961
+08/21 Einmarsch des Warschauer Pakts in die Tschechoslowakei, 1968
+10/03 Offizielles Ende der DDR :-)
+10/07 Gründung der DDR 1949
+10/09 Massendemonstration in Leipzig, 1989
+10/14 Kuba-Krise, 1962
+10/18 Ablösung von Erich Honecker als SED-Generalsekretär, 1989
+11/09 Fall der Berliner Mauer, 1989
+09/09 Mao Tse-Tung gestorben im Alter von 82 Jahren, 1976
+11/10 Sowjetischer Präsident Leonid Breschnew gestorben, Alter 75, 1982
+03/27 Chruschtschow wird sowjetischer Präsident, 1958
+10/12 Chruschtschow schlägt während einer Rede in der UNO mit den
+ Schuhen auf den Tisch, 1960
+
+#endif /* _de_DE_ISO_8859_1_geschichte_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.kirche b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.kirche
new file mode 100644
index 0000000..efa7324
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.kirche
@@ -0,0 +1,32 @@
+/*
+ * Kirche in Deutschland
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_kirche_
+#define _de_DE_ISO_8859_1_kirche_
+
+LANG=de_DE.ISO_8859-1
+
+Easter-46 Aschermittwoch
+Easter-48 Rosenmontag
+Easter-7 Palmsonntag
+
+11/Sun-3 Volkstrauertag (maybe)
+11/Sun-2 Volkstrauertag oder Totensonntag
+11/Sun-1 1. Advent oder Totensonntag
+12/Sun+1 1. oder 2. Advent
+12/Sun+2 2. oder 3. Advent
+12/Sun+3 3. oder 4. Advent
+12/Sun+4 4. Advent (maybe)
+
+12/06 Nikolaus
+12/25 1. Weihnachtstag
+12/26 2. Weihnachtstag
+
+/* Evangelische Kirche */
+11/10 Martin Luther geboren in Eisleben, 1483
+10/31 95 Thesen von Luther, Wittenberg, 1517
+
+#endif /* !_de_DE_ISO_8859_1_kirche_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.literatur b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.literatur
new file mode 100644
index 0000000..865e88e
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.literatur
@@ -0,0 +1,36 @@
+/*
+ * Literatur
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_literatur_
+#define _de_DE_ISO_8859_1_literatur_
+
+LANG=de_DE.ISO_8859-1
+
+/* Schriftsteller
+
+ Fontane
+ Goethe
+ Grass
+ Hegel
+ Heine
+ Schiller
+ */
+
+01/04 Jakob Grimm geboren, 1785
+04/22 Kant geboren, 1724
+07/03 Franz Kafka geboren, 1883
+08/12 Thomas Mann gestorben, 1955
+
+
+/* Verlage */
+03/09 "die tageszeitung" als erste täglich aktualisierte deutsche
+ Tageszeitung im WWW, 1995, Betatest, vollständige Ausgabe
+05/05 Schweriner Volkszeitung als erste deutsche Tageszeitung im WWW, 1995
+05/12 "die tageszeitung" offiziell im WWW, 1995
+08/31 Hitler stellt Frankfurter Zeitung ein, 1943
+11/01 Frankfurter Allgemeine Zeitung in Leben gerufen, 1949
+
+#endif /* !_de_DE_ISO_8859_1_literatur_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.musik b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.musik
new file mode 100644
index 0000000..d08fcd7
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.musik
@@ -0,0 +1,25 @@
+/*
+ * Musik
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_musik_
+#define _de_DE_ISO_8859_1_musik_
+
+LANG=de_DE.ISO_8859-1
+
+/* Klassik */
+02/23 Händel geboren, 1685
+03/21 J.S. Bach geboren, 1685
+05/07 Johannes Brahms geboren in Hamburg, 1833
+05/22 Johann Sebastian Bach geboren in Eisenach, 1665
+07/28 Bach gestorben, 1750
+10/22 Franz Liszt geboren, 1811
+12/05 Mozart gestorben, 1791
+12/16 Beethoven gestorben, 1770
+
+/* Pop */
+09/18 Jimi Hendrix gestorben
+
+#endif /* !_de_DE_ISO_8859_1_musik_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.wissenschaft b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.wissenschaft
new file mode 100644
index 0000000..87f9c72
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.wissenschaft
@@ -0,0 +1,19 @@
+/*
+ * Wissenschaft
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_wissenschaft_
+#define _de_DE_ISO_8859_1_wissenschaft_
+
+LANG=de_DE.ISO_8859-1
+
+04/12 erster Mann im All, Juri Gagarin, 1961
+04/18 Einstein gestorben, 1955
+06/22 Konrad Zuse geboren, 1919, Berlin
+10/04 Sputnik 1, erster Satellit im Weltraum, 1957
+12/18 Konrad Zuse gestorben, 1995, Hünfeld
+
+
+#endif /* ! _de_DE_ISO_8859_1_wissenschaft_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.all b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.all
new file mode 100644
index 0000000..72068aa
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.all
@@ -0,0 +1,17 @@
+/*
+ * deutscher Kalender
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_all_
+#define _de_DE_ISO_8859_1_all_
+
+#include <de_DE.ISO_8859-1/calendar.feiertag>
+#include <de_DE.ISO_8859-1/calendar.geschichte>
+#include <de_DE.ISO_8859-1/calendar.kirche>
+#include <de_DE.ISO_8859-1/calendar.literatur>
+#include <de_DE.ISO_8859-1/calendar.musik>
+#include <de_DE.ISO_8859-1/calendar.wissenschaft>
+
+#endif /* !_de_DE.ISO_8859-1_all_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.feiertag b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.feiertag
new file mode 100644
index 0000000..d723afa
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.feiertag
@@ -0,0 +1,42 @@
+/*
+ * Feiertage
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_feiertag_
+#define _de_DE_ISO_8859_1_feiertag_
+
+LANG=de_DE.ISO_8859-1
+
+/* arbeitsfreie staatliche Feiertage */
+01/01 Neujahr
+05/01 Maifeiertag
+10/03 Tag der deutschen Einheit
+
+/* arbeitsfreie christliche Feiertage */
+Easter-2 Karfreitag
+Easter Ostersonntag
+Easter+1 Ostermontag
+Easter+49 Pfingstsonntag
+Easter+50 Pfingstmontag
+Easter+39 Christi Himmelfahrt
+
+/* Gedenktage - nicht arbeitsfreie Feiertage :-( */
+06/17 Arbeiteraufstand am 17. Juni 1953
+/* ??/?? Befreiung des KZs Auschwitz */
+
+/* Jahreszeiten */
+03/21* Frühlingsanfang
+06/21* Sommeranfang
+09/21* Herbstanfang
+12/21* Winteranfang
+
+/* Sommer- und Winterzeit */
+03/SundayLast Anfang der Sommerzeit
+10/SundayLast Ende der Sommerzeit
+
+/* Blumenverkäufer */
+May Sun+2 Muttertag
+
+#endif /*! _de_DE_ISO_8859_1_feiertag_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.geschichte b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.geschichte
new file mode 100644
index 0000000..6a2a25e
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.geschichte
@@ -0,0 +1,197 @@
+/*
+ * deutsche Geschichte
+ *
+ *
+ * Die Angaben wurden überwiegend entnommen aus dem Buch:
+ *
+ * Fragen an die deutsch Geschichte, Ideen, Kräfte, Entscheidungen von
+ * 1800 bis zur Gegenwart; historische Ausstellung im Reichstagsgebäude
+ * in Berlin; Katalog, 16. Auflage, Sonderausgabe - Bonn: Deutscher
+ * Bundestag, Referat Öffentlichkeitsarbeit, 1990
+ *
+ * English Title: Question on German history
+ *
+ * ISBN 3-924521-59-X
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_geschichte_
+#define _de_DE_ISO_8859_1_geschichte_
+
+LANG=de_DE.ISO_8859-1
+
+/* 1800-1933 */
+07/11 Gründung der Rheinbundes, 1806
+10/14 Doppelschlacht bei Jena und Auerstedt, 1806
+10/16 Völkerschlacht bei Leipzig, 1813
+06/18 Niederlage Napoleons bei Waterloo, 1815
+10/18 Wartburgfest der Deutschen Burschenschaften, 1817
+01/01 Inkrafttreten des Vertrages über den deutschen. Zollverein, 1834
+12/07 erste deutsche Eisenbahn zwischen Nürnberg und Fürth, 1835
+06 Aufstand der schlesischen Weber, 1844
+12/21 Verabschiedung des Gesetzes über die Grundrechte des deutschen
+ Volkes durch die Frankfurter Nationalversammlung, 1848
+03/27 Annahme der deutschen Reichsverfassung in der Frankfurter
+ Paulskirche, Wahl von Friedrich Wilhelm IV von Preußen zum
+ deutschen Kaiser, 1849
+04/28 Ablehnung der deutschen Kaiserkrone durch den preußichen König, 1849
+07/03 Schlacht von Königgrätz
+07/13 Emser Depesche, 1870
+07/18 Verkündung des Dogmas von der päpstlichen Unfehlbarkeit
+ durch das I. Vatikanische Konzil
+01/18 Proklamation des deutschen Kaiserreiches in Versailles, 1871
+10/18 Verabschiedung des Sozialistengesetzes durch den Reichstag, 1878
+03/20 Entlassung von Bismarck als Reichskanzler und
+ preußischer Ministerpräsident, 1890
+06/21 Eröffnung des Nord-Ostsee-Kanals, 1895
+01/07 Billigung des Bürgerlichen Gesetzbuches (BGB) durch den Reichstag, 1896
+01/01 Bürgerliches Gesetzbuches tritt in Kraft, 1900
+06/28 Ermordung des österreichischen Thronfolgers Erzherzog Franz
+ Ferdinand durch serbische Nationalisten in Sarajewo, 1914
+07/28 Kriegserklärung Österreich-Ungarns an Serbien, 1914
+08/01 Deutsche Mobilmachung und Kriegserklärung an Rußland, 1914
+08/03 Deutsche Kriegserklärung an Frankreich, 1914
+08/04 Kriegserklärung Großbritaniens an Deutschland, 1914
+08/04 Bewilligung der Kriegskredite im Reichstag, 1914
+08/26 Schlacht bei Tannenberg, 1914
+02/21 Schlacht um Verdun, 1916
+03/08 Ausbruch der Revolution in Rußland, Abdankung von Zar Nikolaus II, 1917
+04/06 Kriegserklärung der USA an Deutschland
+12/15 Waffenstillstand zwisch Rußland und Deutschland, 1917
+03/03 Frieden von Brest-Litowsk, 1918
+11/03 Matrosenaufstand in Kiel, 1918
+11/09 Ausrufung der Republik durch Scheidemann (SPD), 1918
+02/11 Friedrich Ebert wird Reichspräsident, Weimar 1919
+06/28 Unterzeichnung des Versailler Vertrages, 1919
+03/21 Volksabstimmung in Oberschlesien, 1921
+04/16 Vertrag von Rapallo, 1922
+06/24 Ermordung von Reichsaußenminister Rathenau, 1922
+01/11 Besetzung des Ruhrgebietes durch Frankreich+Belgien, 1923
+09/10 Eintritt Deutschlands in den Völkerbund, 1926
+10/25 Schwarzer Freitag in New York, Beginn der Weltwirtschaftskrise, 1929
+
+
+/* II. Weltkrieg */
+10/14 Austritt auf dem Völkerbund, 1933
+03/16 Wiedereinführung der allgemeinen Wehrpflicht, 1935
+10/25 Deutsch-italienischer Vertrag, Achse Berlin-Rom, 1936
+11/25 Antikominternpakt zwischen Deutschland und Japan, 1936
+01/13 Volksabstimmung im Saargebiet über die Rückführung ins Reich, 1935
+03/12 Einmarsch deutscher Truppen in Österreich, 1938
+09/29 Münchner Abkommen, 1938
+03/15 Einmarsch deutscher Truppen in die Tschechoslowakei, 1939
+03/23 Rückgabe des Memelgebietes an Deutschland, 1939
+08/23 Abschluß des Hitler-Stalin-Paktes, 1939
+09/03 Kriegserklärung Großbritaniens und Frankreichs an Deutschland, 1939
+04/09 Besetzung Dänemarks, Invasion in Norwegen, 1940
+05/10 Deutscher Angriff auf Belgien, die Niederlande, Luxemburg
+ und Frankreich, 1940
+06/22 Angriff gegen die Sowjetunion, 1941
+12/11 Kriegserklärung Deutschlands an die USA, 1941
+01/14 Konferenz von Casablanca, 1943
+01/31 Kapitulation der 6. deutschen Armee in Stalingrad, 1943
+06/06 Alliierte Landung in Nordwestfrankreich, 1944
+02/04 Konferenz von Jalta, 4.-11.2. 1945
+04/25 Zusammentreffen von amerikanischen und sowjetischer Truppen
+ bei Torgau an der Elbe, 1945
+05/08 Bedingungslose Kapitulation von Deutschland, 1945
+07/01 Rückzug der britischen und amerikanischen Truppen aus
+ Sachsen, Thüringen und Mecklenburg, Einmarsch westlicher
+ Truppen in Berlin, 1945
+07/17 Potsdammer Konferenz, 1945
+09/01 Überfall auf Polen, Beginn des 2. Weltkrieges, 1939
+10/01 Verkündigung der Urteile im Nürnberger
+ Hauptkriegsverbrecherprozeß, 1946
+02/25 Auflösung der Landes Preußen durch den Kontrollrat, 1947
+08/06 erster Atombombenabwurf auf Hiroshima, 1945
+08/08 Atombombenabwurf auf Nagasaki, 1945
+04/19 Warschauer Ghetto Aufstand, 1943
+12/07 Japan bombardiert Pearl Harbor, 1941
+
+/* Deutschland nach dem 2. Weltkrieg */
+04/11 Attentat auf Dutschke, Studentenunruhen, 1958
+04/26 GAU in Tschernobyl
+05/05 Natobeitritt, Wiederbewaffnung, Souveränität der Bundesrepublik, 1955
+05/06 Rücktritt von Brandt, 1974
+05/16 Wahl von Schmidt (SPD) zum Bundeskanzler, 1974
+05/23 Verkündung des Grundgesetzes
+05/23 Wahl von Richard von Weizäcker zum Bundespräsidenten, 1984
+06/05 Marshallplan, 1947
+06/20 Währungsreform in den Westzonen, 1948
+06/24 Beginn der Berliner Blockade, 1948
+07/01 Wahl von Heinrich Lübke zum Bundespräsidenten, 1959
+07/01 Währungs- und Währungsunion, 1990
+08/12 deutsch-sowjetischer Gewaltverzichtsvertrag, Moskau 1970
+08/14 Wahl zum ersten deutschen Bundestag, 1949
+09/03 Vier-Mächte-Abkommen über Berlin, 1971
+09/05 Entführung und Ermordung von Arbeitgeberpräsident Schleyer, Entführung einer Lufthansa-Maschine nach Mogadischu, 1977
+09/07 DDR-Staatsratsvorsitzender Honecker in der Bundesrepublik, 1987
+09/12 Wahl von Theodor Heuss (FDP) zum Bundespräsidenten, 1949
+09/15 Wahl von Konrad Adenauer (CDU) zum Bundeskanzler, 1949
+09/17 Bruch der Sozialliberalen Koalition, 1982
+09/18 Aufnahme von Bundesrepublik und DDR in die UNO, 1993
+10/01 Ablösung von Bundeskanzler Schmidt durch Kohl, 1982
+10/23 Volksabstimmung im Saargebiet, 1955
+12/02 Washingtoner Abkommen über Bi-Zone, 1946
+12/07 deutsch-polnischer Vertrag, Warschau 1970
+12/10 Friedensnobelpreis für Brandt, 1971
+12/12 Nachrüstungsbeschluß des NATO-Ministerates, 1979
+12/21 Grundlagenvertrag zwischen DDR und Bundesrepublik, 1972
+
+
+/* Nationalsozialismus */
+11/09 Hitler-Putsch in München/Marsch auf die Feldherrenhalle, 1923
+11/09 Reichskristallnacht, 1938
+09/14 Reichstagswahl: Erdrutsch zugunsten der NSDAP, 1930
+07/31 Reichstagswahl: NSDAP wird stärkste Fraktion, 1932
+11/06 Reichstagswahl: Rückgang der NSDAP, 1932
+01/30 Ernennung von Hitler zum Reichskanzler, 1933
+02/27 Reichstagsbrand, 1933
+03/05 Reichstagswahl: Mehrheit für NSDAP+DNVP, 1933
+03/23 Annahme des Ermächtigungsgesetzes, 1933
+03/31 Erstes Gesetz zur Gleichschaltung der Länder, 1933
+04/01 Organisierter Boykott jüdischer Geschäfte, 1933
+04/07 Zweites Gesetz zur Gleichschaltung der Länder, 1933
+05/02 Auflösung der Gewerkschaften, 1933
+06 Auflösung aller Parteien außer NSDAP, 1933
+07/20 Konkordat zwischen Deutschland und dem Vatikan, 1933
+06/30 Röhm-Putsch, Ausschaltung der SA-Führung, 1934
+09/15 Nürnberger Gesetze, 1935
+01/08 Eröffnung der olympischen Spiele in Berlin, 1936
+01/20 Wannseekonferenz, 1942
+04/30 Selbstmord Hitlers, 1945
+07/29 Mussolini geboren, 1883
+
+/* Sozialismus */
+01/21 Lenin gestorben, 1924
+06 Gründung des Bundes der Kommunisten in London durch Marx+Engels, 1847
+05/23 Gründung des Allgemeinen Deutschen Arbeitervereins in Leipzig
+ unter Führung von Ferdinand Lassalles, 1863
+08/07 Gründung der Sozialdemokratischen Arbeiterpartei in Eisenach
+ unter der Führung von August Begel und Wilhelm Liebknecht, 1869
+04/06 Gründung der Unabhängigen Sozialdemokratischen Partei, Gotha 1917
+11/07 Oktoberrevolution in Rußland, Putsch der Bolschewisten, 1917
+12/31 Gründung der KPD, 1918
+01/15 Ermordung von Rosa Luxemburg und Karl Liebknecht, 1919
+03/05 Tod Stalins, 1953
+03/18 Erste demokratische Volkskammerwahl, 1990
+04/21 Zwangsvereinigung von KPD und SPD zur SED, 1946
+05/14 Gründung der Warschauer Paktes, 1955
+06/17 Arbeiteraufstand am 17. Juni 1953
+06/25 Begin der Korea-Krieges, 1950
+08/13 Bau der Berliner Mauer, 1961
+08/21 Einmarsch des Warschauer Pakts in die Tschechoslowakei, 1968
+10/03 Offizielles Ende der DDR :-)
+10/07 Gründung der DDR 1949
+10/09 Massendemonstration in Leipzig, 1989
+10/14 Kuba-Krise, 1962
+10/18 Ablösung von Erich Honecker als SED-Generalsekretär, 1989
+11/09 Fall der Berliner Mauer, 1989
+09/09 Mao Tse-Tung gestorben im Alter von 82 Jahren, 1976
+11/10 Sowjetischer Präsident Leonid Breschnew gestorben, Alter 75, 1982
+03/27 Chruschtschow wird sowjetischer Präsident, 1958
+10/12 Chruschtschow schlägt während einer Rede in der UNO mit den
+ Schuhen auf den Tisch, 1960
+
+#endif /* _de_DE_ISO_8859_1_geschichte_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.kirche b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.kirche
new file mode 100644
index 0000000..efa7324
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.kirche
@@ -0,0 +1,32 @@
+/*
+ * Kirche in Deutschland
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_kirche_
+#define _de_DE_ISO_8859_1_kirche_
+
+LANG=de_DE.ISO_8859-1
+
+Easter-46 Aschermittwoch
+Easter-48 Rosenmontag
+Easter-7 Palmsonntag
+
+11/Sun-3 Volkstrauertag (maybe)
+11/Sun-2 Volkstrauertag oder Totensonntag
+11/Sun-1 1. Advent oder Totensonntag
+12/Sun+1 1. oder 2. Advent
+12/Sun+2 2. oder 3. Advent
+12/Sun+3 3. oder 4. Advent
+12/Sun+4 4. Advent (maybe)
+
+12/06 Nikolaus
+12/25 1. Weihnachtstag
+12/26 2. Weihnachtstag
+
+/* Evangelische Kirche */
+11/10 Martin Luther geboren in Eisleben, 1483
+10/31 95 Thesen von Luther, Wittenberg, 1517
+
+#endif /* !_de_DE_ISO_8859_1_kirche_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.literatur b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.literatur
new file mode 100644
index 0000000..865e88e
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.literatur
@@ -0,0 +1,36 @@
+/*
+ * Literatur
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_literatur_
+#define _de_DE_ISO_8859_1_literatur_
+
+LANG=de_DE.ISO_8859-1
+
+/* Schriftsteller
+
+ Fontane
+ Goethe
+ Grass
+ Hegel
+ Heine
+ Schiller
+ */
+
+01/04 Jakob Grimm geboren, 1785
+04/22 Kant geboren, 1724
+07/03 Franz Kafka geboren, 1883
+08/12 Thomas Mann gestorben, 1955
+
+
+/* Verlage */
+03/09 "die tageszeitung" als erste täglich aktualisierte deutsche
+ Tageszeitung im WWW, 1995, Betatest, vollständige Ausgabe
+05/05 Schweriner Volkszeitung als erste deutsche Tageszeitung im WWW, 1995
+05/12 "die tageszeitung" offiziell im WWW, 1995
+08/31 Hitler stellt Frankfurter Zeitung ein, 1943
+11/01 Frankfurter Allgemeine Zeitung in Leben gerufen, 1949
+
+#endif /* !_de_DE_ISO_8859_1_literatur_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.musik b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.musik
new file mode 100644
index 0000000..d08fcd7
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.musik
@@ -0,0 +1,25 @@
+/*
+ * Musik
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_musik_
+#define _de_DE_ISO_8859_1_musik_
+
+LANG=de_DE.ISO_8859-1
+
+/* Klassik */
+02/23 Händel geboren, 1685
+03/21 J.S. Bach geboren, 1685
+05/07 Johannes Brahms geboren in Hamburg, 1833
+05/22 Johann Sebastian Bach geboren in Eisenach, 1665
+07/28 Bach gestorben, 1750
+10/22 Franz Liszt geboren, 1811
+12/05 Mozart gestorben, 1791
+12/16 Beethoven gestorben, 1770
+
+/* Pop */
+09/18 Jimi Hendrix gestorben
+
+#endif /* !_de_DE_ISO_8859_1_musik_ */
diff --git a/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.wissenschaft b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.wissenschaft
new file mode 100644
index 0000000..87f9c72
--- /dev/null
+++ b/usr.bin/calendar/calendars/de_DE.ISO_8859-1/calendar.wissenschaft
@@ -0,0 +1,19 @@
+/*
+ * Wissenschaft
+ *
+ * $Id$
+ */
+
+#ifndef _de_DE_ISO_8859_1_wissenschaft_
+#define _de_DE_ISO_8859_1_wissenschaft_
+
+LANG=de_DE.ISO_8859-1
+
+04/12 erster Mann im All, Juri Gagarin, 1961
+04/18 Einstein gestorben, 1955
+06/22 Konrad Zuse geboren, 1919, Berlin
+10/04 Sputnik 1, erster Satellit im Weltraum, 1957
+12/18 Konrad Zuse gestorben, 1995, Hünfeld
+
+
+#endif /* ! _de_DE_ISO_8859_1_wissenschaft_ */
diff --git a/usr.bin/calendar/calendars/hr_HR.ISO8859-2/calendar.all b/usr.bin/calendar/calendars/hr_HR.ISO8859-2/calendar.all
new file mode 100644
index 0000000..17285bd
--- /dev/null
+++ b/usr.bin/calendar/calendars/hr_HR.ISO8859-2/calendar.all
@@ -0,0 +1,12 @@
+/*
+ * hrvatski calendar
+ *
+ * $Id$
+ */
+
+#ifndef _hr_HR_ISO_8859_2_all
+#define _hr_HR_ISO_8859_2_all
+
+#include <hr_HR.ISO_8859-2/calendar.praznici>
+
+#endif /* !_hr_HR_ISO_8859_2_all */
diff --git a/usr.bin/calendar/calendars/hr_HR.ISO8859-2/calendar.praznici b/usr.bin/calendar/calendars/hr_HR.ISO8859-2/calendar.praznici
new file mode 100644
index 0000000..4c978d6
--- /dev/null
+++ b/usr.bin/calendar/calendars/hr_HR.ISO8859-2/calendar.praznici
@@ -0,0 +1,37 @@
+/*
+ * hrvatski praznici
+ *
+ * $Id$
+ */
+
+#ifndef _hr_HR_ISO_8859_2_praznici
+#define _hr_HR_ISO_8859_2_praznici
+
+LANG=hr_HR.ISO_8859-2
+
+/* slobodni dr¾avni praznici */
+01/01 Nova godina
+05/01 Prvi maj
+
+
+/* slobodni kr¹æanski praznici */
+Easter-2 Veliki petak
+Easter Uskrs
+Easter+1 Uskrsni ponedjeljak
+Easter+49 Duhovi
+Easter+50 Duhovni ponedjeljak
+Easter+39 Uza¹a¹æe
+12/25 Bo¾iæ
+12/26 Stjepandan
+
+/* godi¹nja doba */
+03/21* Poèetak proljeæa
+06/21* Poèetak ljeta
+09/21* Poèetak jesena
+12/21* Poèetak zime
+
+/* ljetno vrijeme */
+03/SundayLast Poèetak ljetnog vremena
+10/SundayLast Kraj ljetnog vremena
+
+#endif /* !_hr_HR_ISO_8859_2_praznici */
diff --git a/usr.bin/calendar/calendars/hr_HR.ISO_8859-2/calendar.all b/usr.bin/calendar/calendars/hr_HR.ISO_8859-2/calendar.all
new file mode 100644
index 0000000..17285bd
--- /dev/null
+++ b/usr.bin/calendar/calendars/hr_HR.ISO_8859-2/calendar.all
@@ -0,0 +1,12 @@
+/*
+ * hrvatski calendar
+ *
+ * $Id$
+ */
+
+#ifndef _hr_HR_ISO_8859_2_all
+#define _hr_HR_ISO_8859_2_all
+
+#include <hr_HR.ISO_8859-2/calendar.praznici>
+
+#endif /* !_hr_HR_ISO_8859_2_all */
diff --git a/usr.bin/calendar/calendars/hr_HR.ISO_8859-2/calendar.praznici b/usr.bin/calendar/calendars/hr_HR.ISO_8859-2/calendar.praznici
new file mode 100644
index 0000000..4c978d6
--- /dev/null
+++ b/usr.bin/calendar/calendars/hr_HR.ISO_8859-2/calendar.praznici
@@ -0,0 +1,37 @@
+/*
+ * hrvatski praznici
+ *
+ * $Id$
+ */
+
+#ifndef _hr_HR_ISO_8859_2_praznici
+#define _hr_HR_ISO_8859_2_praznici
+
+LANG=hr_HR.ISO_8859-2
+
+/* slobodni dr¾avni praznici */
+01/01 Nova godina
+05/01 Prvi maj
+
+
+/* slobodni kr¹æanski praznici */
+Easter-2 Veliki petak
+Easter Uskrs
+Easter+1 Uskrsni ponedjeljak
+Easter+49 Duhovi
+Easter+50 Duhovni ponedjeljak
+Easter+39 Uza¹a¹æe
+12/25 Bo¾iæ
+12/26 Stjepandan
+
+/* godi¹nja doba */
+03/21* Poèetak proljeæa
+06/21* Poèetak ljeta
+09/21* Poèetak jesena
+12/21* Poèetak zime
+
+/* ljetno vrijeme */
+03/SundayLast Poèetak ljetnog vremena
+10/SundayLast Kraj ljetnog vremena
+
+#endif /* !_hr_HR_ISO_8859_2_praznici */
diff --git a/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.all b/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.all
new file mode 100644
index 0000000..89548e8
--- /dev/null
+++ b/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.all
@@ -0,0 +1,15 @@
+/*
+ * òÕÓÓËÉÊ ËÁÌÅÎÄÁÒØ
+ *
+ * $Id$
+ */
+
+#ifndef _ru_SU_KOI8_R_all
+#define _ru_SU_KOI8_R_all
+
+#include <ru_SU.KOI8-R/calendar.common>
+#include <ru_SU.KOI8-R/calendar.msk>
+#include <ru_SU.KOI8-R/calendar.pagan>
+#include <ru_SU.KOI8-R/calendar.orthodox>
+
+#endif /* !_ru_SU_KOI8_R_all */
diff --git a/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.common b/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.common
new file mode 100644
index 0000000..6bd7764
--- /dev/null
+++ b/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.common
@@ -0,0 +1,24 @@
+/*
+ * òÕÓÓËÉÅ ÐÒÁÚÄÎÉËÉ
+ *
+ * $Id: calendar.common,v 1.6 1997/05/01 14:39:55 ache Exp $
+ */
+
+#ifndef _ru_SU_KOI8_R_common_
+#define _ru_SU_KOI8_R_common_
+
+LANG=ru_SU.KOI8-R
+
+ 1 ÑÎ× îÏ×ÙÊ çÏÄ
+14 ÑÎ× óÔÁÒÙÊ îÏ×ÙÊ çÏÄ
+23 ÆÅ× äÅÎØ úÁÝÉÔÎÉËÁ ïÔÅÞÅÓÔ×Á
+ 8 ÍÁÒ íÅÖÄÕÎÁÒÏÄÎÙÊ öÅÎÓËÉÊ äÅÎØ
+ 1 ÁÐÒ äÅÎØ óÍÅÈÁ
+ 1 ÍÁÊ ðÒÁÚÄÎÉË ×ÅÓÎÙ É ÔÒÕÄÁ
+ 9 ÍÁÊ äÅÎØ ðÏÂÅÄÙ
+ 1 ÉÀÎ äÅÎØ úÁÝÉÔÙ äÅÔÅÊ
+12 ÉÀÎ äÅÎØ îÅÚÁ×ÉÓÉÍÏÓÔÉ òÏÓÓÉÉ
+ 1 ÓÅÎ äÅÎØ úÎÁÎÉÊ
+
+#endif /* !_ru_SU_KOI8_R_common_ */
+
diff --git a/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.msk b/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.msk
new file mode 100644
index 0000000..25c0eef
--- /dev/null
+++ b/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.msk
@@ -0,0 +1,16 @@
+/*
+ * ðÅÒÅ×ÏÄ ÞÁÓÏ× ÄÌÑ ÍÏÓËÏ×ÓËÏÊ ×ÒÅÍÅÎÎÏÊ ÚÏÎÙ
+ *
+ * $Id$
+ */
+
+#ifndef _ru_SU_KOI8_R_msk_
+#define _ru_SU_KOI8_R_msk_
+
+LANG=ru_SU.KOI8-R
+
+03/SunLast îÁÞÁÌÏ ÍÏÓËÏ×ÓËÏÇÏ ÌÅÔÎÅÇÏ ×ÒÅÍÅÎÉ; ÞÁÓÙ ÐÅÒÅ×ÏÄÑÔÓÑ ×ÐÅÒÅÄ
+10/SunLast ëÏÎÅà ÍÏÓËÏ×ÓËÏÇÏ ÌÅÔÎÅÇÏ ×ÒÅÍÅÎÉ; ÞÁÓÙ ÐÅÒÅ×ÏÄÑÔÓÑ ÎÁÚÁÄ
+
+#endif /* !_ru_SU_KOI8_R_msk_ */
+
diff --git a/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.orthodox b/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.orthodox
new file mode 100644
index 0000000..51ef35b
--- /dev/null
+++ b/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.orthodox
@@ -0,0 +1,34 @@
+/*
+ * ðÒÁ×ÏÓÌÁ×ÎÙÅ ÐÒÁÚÄÎÉËÉ
+ *
+ * $Id$
+ */
+
+#ifndef _ru_SU_KOI8_R_orthodox_
+#define _ru_SU_KOI8_R_orthodox_
+
+LANG=ru_SU.KOI8-R
+Paskha=ðÁÓÈÁ
+
+21 ÓÅÎ òÏÖÄÅÓÔ×Ï ðÒÅÓ×ÑÔÏÊ âÏÇÏÒÏÄÉÃÙ
+28 ÓÅÎ ÷ÏÚÄ×ÉÖÅÎÉÅ ëÒÅÓÔÁ çÏÓÐÏÄÎÑ
+14 ÏËÔ ðÏËÒÏ× ðÒÅÓ×ÑÔÏÊ âÏÇÏÒÏÄÉÃÙ
+ 4 ÄÅË ÷×ÅÄÅÎÉÅ ×Ï ÈÒÁÍ ðÒÅÓ×ÑÔÏÊ âÏÇÏÒÏÄÉÃÙ
+ 7 ÑÎ× òÏÖÄÅÓÔ×Ï èÒÉÓÔÏ×Ï
+19 ÑÎ× âÏÇÏÑ×ÌÅÎÉÅ ÉÌÉ ëÒÅÝÅÎÉÅ çÏÓÐÏÄÎÅ
+15 ÆÅ× óÒÅÔÅÎÉÅ çÏÓÐÏÄÎÅ
+ðÁÓÈÁ-46 ÷ÅÌÉËÉÊ ðÏÓÔ
+ðÁÓÈÁ-7 ÷ÅÒÂÎÏÅ ÷ÏÓËÒÅÓÅÎØÅ
+ðÁÓÈÁ-3 ÷ÅÌÉËÉÊ þÅÔ×ÅÒÇ
+ðÁÓÈÁ-2 óÔÒÁÓÔÎÁÑ ðÑÔÎÉÃÁ
+ðÁÓÈÁ ÷ÏÓËÒÅÓÅÎÉÅ èÒÉÓÔÏ×Ï
+ðÁÓÈÁ+39 ÷ÏÚÎÅÓÅÎÉÅ
+ðÁÓÈÁ+49 ðÑÔÉÄÅÓÑÔÎÉÃÁ
+ðÁÓÈÁ+56 ôÒÏÉÃÉÎ äÅÎØ
+ðÁÓÈÁ+60 ðÒÁÚÄÎÉË ôÅÌÁ èÒÉÓÔÏ×Á
+ 7 ÁÐÒ âÌÁÇÏ×ÅÝÅÎÉÅ ðÒÅÓ×ÑÔÏÊ âÏÇÏÒÏÄÉÃÙ
+19 Á×Ç ðÒÅÏÂÒÁÖÅÎÉÅ çÏÓÐÏÄÎÅ
+28 Á×Ç õÓÐÅÎÉÅ ðÒÅÓ×ÑÔÏÊ âÏÇÏÒÏÄÉÃÙ
+
+#endif /* !_ru_SU_KOI8_R_orthodox_ */
+
diff --git a/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.pagan b/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.pagan
new file mode 100644
index 0000000..993a32d
--- /dev/null
+++ b/usr.bin/calendar/calendars/ru_RU.KOI8-R/calendar.pagan
@@ -0,0 +1,42 @@
+/*
+ * ñÚÙÞÅÓËÉÅ ÐÒÁÚÄÎÉËÉ
+ *
+ * $Id$
+ */
+
+#ifndef _ru_SU_KOI8_R_pagan_
+#define _ru_SU_KOI8_R_pagan_
+
+LANG=ru_SU.KOI8-R
+Paskha=ðÁÓÈÁ
+
+21 ÄÅË* úÉÍÎÅÅ ÓÏÌÎÃÅÓÔÏÑÎÉÅ
+25 ÄÅË ëÏÌÑÄÁ (ÓÄ×ÉÎÕÔÏÅ ÚÉÍÎÅÅ ÓÏÌÎÃÅÓÔÏÑÎÉÅ)
+31 ÄÅË îÅÄÅÌÑ ðÒÁÏÔÃÏ×
+ 6 ÑÎ× äÅÎØ ëÁÝÅÑ É ÷ÅÌÅÓÁ, ÐÏÓÔ
+24 ÆÅ× äÅÎØ ÷ÅÌÅÓÁ
+29 ÆÅ× äÅÎØ ëÁÝÅÑ
+ 1 ÍÁÒ äÅÎØ íÁÒÅÎÙ
+14 ÍÁÒ îÏ×ÙÊ çÏÄ, ï×ÓÅÎØ ÍÁÌÙÊ
+ðÁÓÈÁ-47 íÁÓÌÅÎÉÃÁ
+ðÁÓÈÁ+7 ëÒÁÓÎÁÑ çÏÒËÁ
+ðÁÓÈÁ+16 òÁÄÕÎÉÃÁ
+20 ÍÁÒ* ÷ÅÓÅÎÎÉÅ ÒÁ×ÎÏÄÅÎÓÔ×ÉÅ
+ 7 ÁÐÒ äÅÎØ íÁÒÅÎÙ (ÓÄ×ÉÎÕÔÏÅ ×ÅÓÅÎÎÅÅ ÒÁ×ÎÏÄÅÎÓÔ×ÉÅ)
+ 6 ÍÁÊ äÅÎØ äÁÖØÂÏÇÁ, ï×ÓÅÎØ ÂÏÌØÛÏÊ
+22 ÍÁÊ ñÒÉÌÉÎ äÅÎØ
+21 ÉÀÎ* ìÅÔÎÅÅ ÓÏÌÎÃÅÓÔÏÑÎÉÅ
+ 1 ÉÀÌ òÕÓÁÌØÎÁÑ îÅÄÅÌÑ
+ 7 ÉÀÌ ëÕÐÁÌÁ (ÓÄ×ÉÎÕÔÏÅ ÌÅÔÎÅÅ ÓÏÌÎÃÅÓÔÏÑÎÉÅ)
+27 ÉÀÌ ïÔÂÏÒ ÖÅÒÔ× ðÅÒÕÎÕ, ÒÕÓÁÌÉÉ
+ 2 Á×Ç ðÅÒÕÎÏ× äÅÎØ
+21 Á×Ç äÅÎØ óÔÒÉÂÏÇÁ
+28 Á×Ç õÓÐÅÎÉÅ úÌÁÔÏÇÏÒËÉ
+14 ÓÅÎ äÅÎØ ÷ÏÌÈÁ úÍÅÅ×ÉÞÁ
+22 ÓÅÎ* ðÏ×ÏÒÏÔ Ë ÚÉÍÅ (ÏÓÅÎÎÅÅ ÒÁ×ÎÏÄÅÎÓÔ×ÉÅ)
+10 ÎÏÑ äÅÎØ íÁËÏÛÉ
+21 ÎÏÑ äÅÎØ ó×ÁÒÏÇÁ É óÅÍÁÒÇÌÁ
+ 9 ÄÅË äÅÎØ äÁÖØÂÏÇÁ É íÁÒÅÎÙ
+
+#endif /* !_ru_SU_KOI8_R_pagan_ */
+
diff --git a/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.all b/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.all
new file mode 100644
index 0000000..89548e8
--- /dev/null
+++ b/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.all
@@ -0,0 +1,15 @@
+/*
+ * òÕÓÓËÉÊ ËÁÌÅÎÄÁÒØ
+ *
+ * $Id$
+ */
+
+#ifndef _ru_SU_KOI8_R_all
+#define _ru_SU_KOI8_R_all
+
+#include <ru_SU.KOI8-R/calendar.common>
+#include <ru_SU.KOI8-R/calendar.msk>
+#include <ru_SU.KOI8-R/calendar.pagan>
+#include <ru_SU.KOI8-R/calendar.orthodox>
+
+#endif /* !_ru_SU_KOI8_R_all */
diff --git a/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.common b/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.common
new file mode 100644
index 0000000..6bd7764
--- /dev/null
+++ b/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.common
@@ -0,0 +1,24 @@
+/*
+ * òÕÓÓËÉÅ ÐÒÁÚÄÎÉËÉ
+ *
+ * $Id: calendar.common,v 1.6 1997/05/01 14:39:55 ache Exp $
+ */
+
+#ifndef _ru_SU_KOI8_R_common_
+#define _ru_SU_KOI8_R_common_
+
+LANG=ru_SU.KOI8-R
+
+ 1 ÑÎ× îÏ×ÙÊ çÏÄ
+14 ÑÎ× óÔÁÒÙÊ îÏ×ÙÊ çÏÄ
+23 ÆÅ× äÅÎØ úÁÝÉÔÎÉËÁ ïÔÅÞÅÓÔ×Á
+ 8 ÍÁÒ íÅÖÄÕÎÁÒÏÄÎÙÊ öÅÎÓËÉÊ äÅÎØ
+ 1 ÁÐÒ äÅÎØ óÍÅÈÁ
+ 1 ÍÁÊ ðÒÁÚÄÎÉË ×ÅÓÎÙ É ÔÒÕÄÁ
+ 9 ÍÁÊ äÅÎØ ðÏÂÅÄÙ
+ 1 ÉÀÎ äÅÎØ úÁÝÉÔÙ äÅÔÅÊ
+12 ÉÀÎ äÅÎØ îÅÚÁ×ÉÓÉÍÏÓÔÉ òÏÓÓÉÉ
+ 1 ÓÅÎ äÅÎØ úÎÁÎÉÊ
+
+#endif /* !_ru_SU_KOI8_R_common_ */
+
diff --git a/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.msk b/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.msk
new file mode 100644
index 0000000..25c0eef
--- /dev/null
+++ b/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.msk
@@ -0,0 +1,16 @@
+/*
+ * ðÅÒÅ×ÏÄ ÞÁÓÏ× ÄÌÑ ÍÏÓËÏ×ÓËÏÊ ×ÒÅÍÅÎÎÏÊ ÚÏÎÙ
+ *
+ * $Id$
+ */
+
+#ifndef _ru_SU_KOI8_R_msk_
+#define _ru_SU_KOI8_R_msk_
+
+LANG=ru_SU.KOI8-R
+
+03/SunLast îÁÞÁÌÏ ÍÏÓËÏ×ÓËÏÇÏ ÌÅÔÎÅÇÏ ×ÒÅÍÅÎÉ; ÞÁÓÙ ÐÅÒÅ×ÏÄÑÔÓÑ ×ÐÅÒÅÄ
+10/SunLast ëÏÎÅà ÍÏÓËÏ×ÓËÏÇÏ ÌÅÔÎÅÇÏ ×ÒÅÍÅÎÉ; ÞÁÓÙ ÐÅÒÅ×ÏÄÑÔÓÑ ÎÁÚÁÄ
+
+#endif /* !_ru_SU_KOI8_R_msk_ */
+
diff --git a/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.orthodox b/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.orthodox
new file mode 100644
index 0000000..51ef35b
--- /dev/null
+++ b/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.orthodox
@@ -0,0 +1,34 @@
+/*
+ * ðÒÁ×ÏÓÌÁ×ÎÙÅ ÐÒÁÚÄÎÉËÉ
+ *
+ * $Id$
+ */
+
+#ifndef _ru_SU_KOI8_R_orthodox_
+#define _ru_SU_KOI8_R_orthodox_
+
+LANG=ru_SU.KOI8-R
+Paskha=ðÁÓÈÁ
+
+21 ÓÅÎ òÏÖÄÅÓÔ×Ï ðÒÅÓ×ÑÔÏÊ âÏÇÏÒÏÄÉÃÙ
+28 ÓÅÎ ÷ÏÚÄ×ÉÖÅÎÉÅ ëÒÅÓÔÁ çÏÓÐÏÄÎÑ
+14 ÏËÔ ðÏËÒÏ× ðÒÅÓ×ÑÔÏÊ âÏÇÏÒÏÄÉÃÙ
+ 4 ÄÅË ÷×ÅÄÅÎÉÅ ×Ï ÈÒÁÍ ðÒÅÓ×ÑÔÏÊ âÏÇÏÒÏÄÉÃÙ
+ 7 ÑÎ× òÏÖÄÅÓÔ×Ï èÒÉÓÔÏ×Ï
+19 ÑÎ× âÏÇÏÑ×ÌÅÎÉÅ ÉÌÉ ëÒÅÝÅÎÉÅ çÏÓÐÏÄÎÅ
+15 ÆÅ× óÒÅÔÅÎÉÅ çÏÓÐÏÄÎÅ
+ðÁÓÈÁ-46 ÷ÅÌÉËÉÊ ðÏÓÔ
+ðÁÓÈÁ-7 ÷ÅÒÂÎÏÅ ÷ÏÓËÒÅÓÅÎØÅ
+ðÁÓÈÁ-3 ÷ÅÌÉËÉÊ þÅÔ×ÅÒÇ
+ðÁÓÈÁ-2 óÔÒÁÓÔÎÁÑ ðÑÔÎÉÃÁ
+ðÁÓÈÁ ÷ÏÓËÒÅÓÅÎÉÅ èÒÉÓÔÏ×Ï
+ðÁÓÈÁ+39 ÷ÏÚÎÅÓÅÎÉÅ
+ðÁÓÈÁ+49 ðÑÔÉÄÅÓÑÔÎÉÃÁ
+ðÁÓÈÁ+56 ôÒÏÉÃÉÎ äÅÎØ
+ðÁÓÈÁ+60 ðÒÁÚÄÎÉË ôÅÌÁ èÒÉÓÔÏ×Á
+ 7 ÁÐÒ âÌÁÇÏ×ÅÝÅÎÉÅ ðÒÅÓ×ÑÔÏÊ âÏÇÏÒÏÄÉÃÙ
+19 Á×Ç ðÒÅÏÂÒÁÖÅÎÉÅ çÏÓÐÏÄÎÅ
+28 Á×Ç õÓÐÅÎÉÅ ðÒÅÓ×ÑÔÏÊ âÏÇÏÒÏÄÉÃÙ
+
+#endif /* !_ru_SU_KOI8_R_orthodox_ */
+
diff --git a/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.pagan b/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.pagan
new file mode 100644
index 0000000..993a32d
--- /dev/null
+++ b/usr.bin/calendar/calendars/ru_SU.KOI8-R/calendar.pagan
@@ -0,0 +1,42 @@
+/*
+ * ñÚÙÞÅÓËÉÅ ÐÒÁÚÄÎÉËÉ
+ *
+ * $Id$
+ */
+
+#ifndef _ru_SU_KOI8_R_pagan_
+#define _ru_SU_KOI8_R_pagan_
+
+LANG=ru_SU.KOI8-R
+Paskha=ðÁÓÈÁ
+
+21 ÄÅË* úÉÍÎÅÅ ÓÏÌÎÃÅÓÔÏÑÎÉÅ
+25 ÄÅË ëÏÌÑÄÁ (ÓÄ×ÉÎÕÔÏÅ ÚÉÍÎÅÅ ÓÏÌÎÃÅÓÔÏÑÎÉÅ)
+31 ÄÅË îÅÄÅÌÑ ðÒÁÏÔÃÏ×
+ 6 ÑÎ× äÅÎØ ëÁÝÅÑ É ÷ÅÌÅÓÁ, ÐÏÓÔ
+24 ÆÅ× äÅÎØ ÷ÅÌÅÓÁ
+29 ÆÅ× äÅÎØ ëÁÝÅÑ
+ 1 ÍÁÒ äÅÎØ íÁÒÅÎÙ
+14 ÍÁÒ îÏ×ÙÊ çÏÄ, ï×ÓÅÎØ ÍÁÌÙÊ
+ðÁÓÈÁ-47 íÁÓÌÅÎÉÃÁ
+ðÁÓÈÁ+7 ëÒÁÓÎÁÑ çÏÒËÁ
+ðÁÓÈÁ+16 òÁÄÕÎÉÃÁ
+20 ÍÁÒ* ÷ÅÓÅÎÎÉÅ ÒÁ×ÎÏÄÅÎÓÔ×ÉÅ
+ 7 ÁÐÒ äÅÎØ íÁÒÅÎÙ (ÓÄ×ÉÎÕÔÏÅ ×ÅÓÅÎÎÅÅ ÒÁ×ÎÏÄÅÎÓÔ×ÉÅ)
+ 6 ÍÁÊ äÅÎØ äÁÖØÂÏÇÁ, ï×ÓÅÎØ ÂÏÌØÛÏÊ
+22 ÍÁÊ ñÒÉÌÉÎ äÅÎØ
+21 ÉÀÎ* ìÅÔÎÅÅ ÓÏÌÎÃÅÓÔÏÑÎÉÅ
+ 1 ÉÀÌ òÕÓÁÌØÎÁÑ îÅÄÅÌÑ
+ 7 ÉÀÌ ëÕÐÁÌÁ (ÓÄ×ÉÎÕÔÏÅ ÌÅÔÎÅÅ ÓÏÌÎÃÅÓÔÏÑÎÉÅ)
+27 ÉÀÌ ïÔÂÏÒ ÖÅÒÔ× ðÅÒÕÎÕ, ÒÕÓÁÌÉÉ
+ 2 Á×Ç ðÅÒÕÎÏ× äÅÎØ
+21 Á×Ç äÅÎØ óÔÒÉÂÏÇÁ
+28 Á×Ç õÓÐÅÎÉÅ úÌÁÔÏÇÏÒËÉ
+14 ÓÅÎ äÅÎØ ÷ÏÌÈÁ úÍÅÅ×ÉÞÁ
+22 ÓÅÎ* ðÏ×ÏÒÏÔ Ë ÚÉÍÅ (ÏÓÅÎÎÅÅ ÒÁ×ÎÏÄÅÎÓÔ×ÉÅ)
+10 ÎÏÑ äÅÎØ íÁËÏÛÉ
+21 ÎÏÑ äÅÎØ ó×ÁÒÏÇÁ É óÅÍÁÒÇÌÁ
+ 9 ÄÅË äÅÎØ äÁÖØÂÏÇÁ É íÁÒÅÎÙ
+
+#endif /* !_ru_SU_KOI8_R_pagan_ */
+
diff --git a/usr.bin/calendar/day.c b/usr.bin/calendar/day.c
new file mode 100644
index 0000000..91b3731
--- /dev/null
+++ b/usr.bin/calendar/day.c
@@ -0,0 +1,487 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+
+#include <ctype.h>
+#include <locale.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <sys/uio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <err.h>
+
+#include "pathnames.h"
+#include "calendar.h"
+
+struct tm *tp;
+int *cumdays, offset, yrdays;
+char dayname[10];
+
+
+/* 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 },
+};
+
+static char *days[] = {
+ "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL,
+};
+
+static char *months[] = {
+ "jan", "feb", "mar", "apr", "may", "jun",
+ "jul", "aug", "sep", "oct", "nov", "dec", NULL,
+};
+
+static struct fixs fndays[8]; /* full national days names */
+static struct fixs ndays[8]; /* short national days names */
+
+static struct fixs fnmonths[13]; /* full national months names */
+static struct fixs nmonths[13]; /* short national month names */
+
+
+void setnnames(void)
+{
+ char buf[80];
+ int i, l;
+ struct tm tm;
+
+ for (i = 0; i < 7; i++) {
+ tm.tm_wday = i;
+ strftime(buf, sizeof(buf), "%a", &tm);
+ for (l = strlen(buf);
+ l > 0 && isspace((unsigned char)buf[l - 1]);
+ l--)
+ ;
+ buf[l] = '\0';
+ if (ndays[i].name != NULL)
+ free(ndays[i].name);
+ if ((ndays[i].name = strdup(buf)) == NULL)
+ errx(1, "cannot allocate memory");
+ ndays[i].len = strlen(buf);
+
+ strftime(buf, sizeof(buf), "%A", &tm);
+ for (l = strlen(buf);
+ l > 0 && isspace((unsigned char)buf[l - 1]);
+ l--)
+ ;
+ buf[l] = '\0';
+ if (fndays[i].name != NULL)
+ free(fndays[i].name);
+ if ((fndays[i].name = strdup(buf)) == NULL)
+ errx(1, "cannot allocate memory");
+ fndays[i].len = strlen(buf);
+ }
+
+ for (i = 0; i < 12; i++) {
+ tm.tm_mon = i;
+ strftime(buf, sizeof(buf), "%b", &tm);
+ for (l = strlen(buf);
+ l > 0 && isspace((unsigned char)buf[l - 1]);
+ l--)
+ ;
+ buf[l] = '\0';
+ if (nmonths[i].name != NULL)
+ free(nmonths[i].name);
+ if ((nmonths[i].name = strdup(buf)) == NULL)
+ errx(1, "cannot allocate memory");
+ nmonths[i].len = strlen(buf);
+
+ strftime(buf, sizeof(buf), "%B", &tm);
+ for (l = strlen(buf);
+ l > 0 && isspace((unsigned char)buf[l - 1]);
+ l--)
+ ;
+ buf[l] = '\0';
+ if (fnmonths[i].name != NULL)
+ free(fnmonths[i].name);
+ if ((fnmonths[i].name = strdup(buf)) == NULL)
+ errx(1, "cannot allocate memory");
+ fnmonths[i].len = strlen(buf);
+ }
+}
+
+void
+settime(now)
+ time_t 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;
+
+ (void) setlocale(LC_TIME, "C");
+ header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp);
+ (void) setlocale(LC_TIME, "");
+
+ setnnames();
+}
+
+/* convert Day[/Month][/Year] into unix time (since 1970)
+ * Day: two digits, Month: two digits, Year: digits
+ */
+time_t Mktime (dp)
+ char *dp;
+{
+ char *date;
+ time_t t;
+ int len;
+ struct tm tm;
+
+ if ((date = strdup(dp)) == NULL)
+ errx(1, "strdup failed in Mktime");
+ (void)time(&t);
+ tp = localtime(&t);
+
+ len = strlen(date);
+ tm.tm_sec = 0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+ tm.tm_wday = 0;
+ tm.tm_mday = tp->tm_mday;
+ tm.tm_mon = tp->tm_mon;
+ tm.tm_year = tp->tm_year;
+
+
+ /* day */
+ *(date+2) = NULL;
+ tm.tm_mday = atoi(date);
+
+ /* month */
+ if (len >= 4) {
+ *(date+5) = NULL;
+ tm.tm_mon = atoi(date+3) - 1;
+ }
+
+ /* Year */
+ if (len >= 7) {
+ tm.tm_year = atoi(date+6);
+
+ /* tm_year up 1900 ... */
+ if (tm.tm_year > 1900)
+ tm.tm_year -= 1900;
+ }
+
+#if DEBUG
+ printf("Mktime: %d %d %d %s\n", (int)mktime(&tm), (int)t, len,
+ asctime(&tm));
+#endif
+ free(date);
+ return(mktime(&tm));
+}
+
+/*
+ * 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, monthp, dayp, varp)
+ char *endp;
+ int *monthp;
+ int *dayp;
+ int *varp;
+{
+ int day, flags, month = 0, v1, v2;
+
+ /*
+ * CONVENTION
+ *
+ * Month: 1-12
+ * Monthname: Jan .. Dec
+ * Day: 1-31
+ * Weekday: Mon-Sun
+ *
+ */
+
+ flags = 0;
+
+ /* read first field */
+ /* didn't recognize anything, skip it */
+ if (!(v1 = getfield(endp, &endp, &flags)))
+ return (0);
+
+ /* Easter or Easter depending days */
+ if (flags & F_EASTER)
+ day = v1 - 1; /* days since January 1 [0-365] */
+
+ /*
+ * 1. {Weekday,Day} XYZ ...
+ *
+ * where Day is > 12
+ */
+ else if (flags & F_ISDAY || v1 > 12) {
+
+ /* found a day; day: 1-31 or weekday: 1-7 */
+ day = v1;
+
+ /* {Day,Weekday} {Month,Monthname} ... */
+ /* if no recognizable month, assume just a day alone
+ * in other words, find month or use current month */
+ if (!(month = getfield(endp, &endp, &flags)))
+ month = tp->tm_mon + 1;
+ }
+
+ /* 2. {Monthname} XYZ ... */
+ else if (flags & F_ISMONTH) {
+ month = v1;
+
+ /* Monthname {day,weekday} */
+ /* if no recognizable day, assume the first day in month */
+ if (!(day = getfield(endp, &endp, &flags)))
+ day = 1;
+ }
+
+ /* Hm ... */
+ else {
+ v2 = getfield(endp, &endp, &flags);
+
+ /*
+ * {Day} {Monthname} ...
+ * where Day <= 12
+ */
+ if (flags & F_ISMONTH) {
+ day = v1;
+ month = v2;
+ *varp = 0;
+ }
+
+ /* {Month} {Weekday,Day} ... */
+ else {
+ /* F_ISDAY set, v2 > 12, or no way to tell */
+ month = v1;
+ /* if no recognizable day, assume the first */
+ day = v2 ? v2 : 1;
+ *varp = 0;
+ }
+ }
+
+ /* convert Weekday into *next* Day,
+ * e.g.: 'Sunday' -> 22
+ * 'SunayLast' -> ??
+ */
+ if (flags & F_ISDAY) {
+#if DEBUG
+ fprintf(stderr, "\nday: %d %s month %d\n", day, endp, month);
+#endif
+
+ *varp = 1;
+ /* variable weekday, SundayLast, MondayFirst ... */
+ if (day < 0 || day >= 10) {
+
+ /* negative offset; last, -4 .. -1 */
+ if (day < 0) {
+ v1 = day/10 - 1; /* offset -4 ... -1 */
+ day = 10 + (day % 10); /* day 1 ... 7 */
+
+ /* day, eg '22th' */
+ v2 = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
+
+ /* (month length - day) / 7 + 1 */
+ if (((int)((cumdays[month+1] -
+ cumdays[month] - v2) / 7) + 1) == -v1)
+ /* bingo ! */
+ day = v2;
+
+ /* set to yesterday */
+ else {
+ day = tp->tm_mday - 1;
+ if (day == 0)
+ return (0);
+ }
+ }
+
+ /* first, second ... +1 ... +5 */
+ else {
+ v1 = day/10; /* offset: +1 (first Sunday) ... */
+ day = day % 10;
+
+ /* day, eg '22th' */
+ v2 = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
+
+ /* Hurrah! matched */
+ if ( ((v2 - 1 + 7) / 7) == v1 )
+ day = v2;
+
+ /* set to yesterday */
+ else {
+ day = tp->tm_mday - 1;
+ if (day == 0)
+ return (0);
+ }
+ }
+ }
+
+ /* wired */
+ else {
+ day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
+ *varp = 1;
+ }
+ }
+
+ if (!(flags & F_EASTER)) {
+ *monthp = month;
+ *dayp = day;
+ day = cumdays[month] + day;
+ }
+ else {
+ for (v1 = 0; day > cumdays[v1]; v1++)
+ ;
+ *monthp = v1 - 1;
+ *dayp = day - cumdays[v1 - 1];
+ *varp = 1;
+ }
+
+#if DEBUG
+ fprintf(stderr, "day2: day %d(%d) yday %d\n", *dayp, day, tp->tm_yday);
+#endif
+ /* if today or today + offset days */
+ if (day >= tp->tm_yday - f_dayBefore &&
+ day <= tp->tm_yday + offset + f_dayAfter)
+ return (1);
+
+ /* if number of days left in this year + days to event in next year */
+ if (yrdays - tp->tm_yday + day <= offset + f_dayAfter ||
+ /* a year backward, eg. 6 Jan and 10 days before -> 27. Dec */
+ tp->tm_yday + day - f_dayBefore < 0
+ )
+ return (1);
+ return (0);
+}
+
+
+int
+getmonth(s)
+ register char *s;
+{
+ register char **p;
+ struct fixs *n;
+
+ for (n = fnmonths; n->name; ++n)
+ if (!strncasecmp(s, n->name, n->len))
+ return ((n - fnmonths) + 1);
+ for (n = nmonths; n->name; ++n)
+ if (!strncasecmp(s, n->name, n->len))
+ return ((n - nmonths) + 1);
+ for (p = months; *p; ++p)
+ if (!strncasecmp(s, *p, 3))
+ return ((p - months) + 1);
+ return (0);
+}
+
+
+int
+getday(s)
+ register char *s;
+{
+ register char **p;
+ struct fixs *n;
+
+ for (n = fndays; n->name; ++n)
+ if (!strncasecmp(s, n->name, n->len))
+ return ((n - fndays) + 1);
+ for (n = ndays; n->name; ++n)
+ if (!strncasecmp(s, n->name, n->len))
+ return ((n - ndays) + 1);
+ for (p = days; *p; ++p)
+ if (!strncasecmp(s, *p, 3))
+ return ((p - days) + 1);
+ return (0);
+}
+
+/* return offset for variable weekdays
+ * -1 -> last weekday in month
+ * +1 -> first weekday in month
+ * ... etc ...
+ */
+int
+getdayvar(s)
+ register char *s;
+{
+ register int offset;
+
+
+ offset = strlen(s);
+
+
+ /* Sun+1 or Wednesday-2
+ * ^ ^ */
+
+ /* printf ("x: %s %s %d\n", s, s + offset - 2, offset); */
+ switch(*(s + offset - 2)) {
+ case '-':
+ return(-(atoi(s + offset - 1)));
+ break;
+ case '+':
+ return(atoi(s + offset - 1));
+ break;
+ }
+
+
+ /*
+ * some aliases: last, first, second, third, fourth
+ */
+
+ /* last */
+ if (offset > 4 && !strcasecmp(s + offset - 4, "last"))
+ return(-1);
+ else if (offset > 5 && !strcasecmp(s + offset - 5, "first"))
+ return(+1);
+ else if (offset > 6 && !strcasecmp(s + offset - 6, "second"))
+ return(+2);
+ else if (offset > 5 && !strcasecmp(s + offset - 5, "third"))
+ return(+3);
+ else if (offset > 6 && !strcasecmp(s + offset - 6, "fourth"))
+ return(+4);
+
+
+ /* no offset detected */
+ return(0);
+}
diff --git a/usr.bin/calendar/io.c b/usr.bin/calendar/io.c
new file mode 100644
index 0000000..9634dac
--- /dev/null
+++ b/usr.bin/calendar/io.c
@@ -0,0 +1,356 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <sys/wait.h>
+
+#include "pathnames.h"
+#include "calendar.h"
+
+
+char *calendarFile = "calendar"; /* default calendar file */
+char *calendarHome = ".calendar"; /* HOME */
+char *calendarNoMail = "nomail"; /* don't sent mail if this file exist */
+
+struct fixs neaster, npaskha;
+
+struct iovec header[] = {
+ {"From: ", 6},
+ {NULL, 0},
+ {" (Reminder Service)\nTo: ", 24},
+ {NULL, 0},
+ {"\nSubject: ", 10},
+ {NULL, 0},
+ {"'s Calendar\nPrecedence: bulk\n\n", 30},
+};
+
+
+void
+cal()
+{
+ register int printing;
+ register char *p;
+ FILE *fp;
+ int ch, l;
+ int month;
+ int day;
+ int var;
+ 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);
+ for (l = strlen(buf);
+ l > 0 && isspace((unsigned char)buf[l - 1]);
+ l--)
+ ;
+ buf[l] = '\0';
+ if (buf[0] == '\0')
+ continue;
+ if (strncmp(buf, "LANG=", 5) == 0) {
+ (void) setlocale(LC_ALL, buf + 5);
+ setnnames();
+ continue;
+ }
+ if (strncasecmp(buf, "Easter=", 7) == 0 && buf[7]) {
+ if (neaster.name != NULL)
+ free(neaster.name);
+ if ((neaster.name = strdup(buf + 7)) == NULL)
+ errx(1, "cannot allocate memory");
+ neaster.len = strlen(buf + 7);
+ continue;
+ }
+ if (strncasecmp(buf, "Paskha=", 7) == 0 && buf[7]) {
+ if (npaskha.name != NULL)
+ free(npaskha.name);
+ if ((npaskha.name = strdup(buf + 7)) == NULL)
+ errx(1, "cannot allocate memory");
+ npaskha.len = strlen(buf + 7);
+ continue;
+ }
+ if (buf[0] != '\t') {
+ printing = isnow(buf, &month, &day, &var) ? 1 : 0;
+ if ((p = strchr(buf, '\t')) == NULL)
+ continue;
+ if (p > buf && p[-1] == '*')
+ var = 1;
+ if (printing) {
+ struct tm tm;
+ char dbuf[30];
+
+ tm.tm_sec = 0; /* unused */
+ tm.tm_min = 0; /* unused */
+ tm.tm_hour = 0; /* unused */
+ tm.tm_wday = 0; /* unused */
+ tm.tm_mon = month - 1;
+ tm.tm_mday = day;
+ tm.tm_year = tp->tm_year; /* unused */
+ (void)strftime(dbuf, sizeof(dbuf), "%c", &tm);
+ dbuf[10] = '\0';
+ (void)fprintf(fp, "%s%c%s\n",
+ dbuf + 4/* skip weekdays */,
+ var ? '*' : ' ', p);
+ }
+ }
+ else if (printing)
+ fprintf(fp, "%s\n", buf);
+ }
+ closecal(fp);
+}
+
+int
+getfield(p, endp, flags)
+ char *p, **endp;
+ int *flags;
+{
+ int val, var;
+ char *start, savech;
+
+ for (; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) && *p != '*'; ++p);
+ if (*p == '*') { /* `*' is current month */
+ *flags |= F_ISMONTH;
+ *endp = p+1;
+ return (tp->tm_mon + 1);
+ }
+ if (isdigit((unsigned char)*p)) {
+ val = strtol(p, &p, 10); /* if 0, it's failure */
+ for (; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) && *p != '*'; ++p);
+ *endp = p;
+ return (val);
+ }
+ for (start = p; isalpha((unsigned char)*++p););
+
+ /* Sunday-1 */
+ if (*p == '+' || *p == '-')
+ for(; isdigit((unsigned char)*++p););
+
+ savech = *p;
+ *p = '\0';
+
+ /* Month */
+ if ((val = getmonth(start)) != 0)
+ *flags |= F_ISMONTH;
+
+ /* Day */
+ else if ((val = getday(start)) != 0) {
+ *flags |= F_ISDAY;
+
+ /* variable weekday */
+ if ((var = getdayvar(start)) != 0) {
+ if (var <=5 && var >= -4)
+ val += var * 10;
+#ifdef DEBUG
+ printf("var: %d\n", var);
+#endif
+ }
+ }
+
+ /* Easter */
+ else if ((val = geteaster(start, tp->tm_year + 1900)) != 0)
+ *flags |= F_EASTER;
+
+ /* Paskha */
+ else if ((val = getpaskha(start, tp->tm_year + 1900)) != 0)
+ *flags |= F_EASTER;
+
+ /* undefined rest */
+ else {
+ *p = savech;
+ return (0);
+ }
+ for (*p = savech; !isdigit((unsigned char)*p) && !isalpha((unsigned char)*p) && *p != '*'; ++p);
+ *endp = p;
+ return (val);
+}
+
+char path[MAXPATHLEN + 1];
+
+FILE *
+opencal()
+{
+ uid_t uid;
+ int fd, pdes[2];
+ struct stat sbuf;
+
+ /* open up calendar file as stdin */
+ if (!freopen(calendarFile, "r", stdin)) {
+ if (doall) {
+ if (chdir(calendarHome) != 0)
+ return (NULL);
+ if (stat(calendarNoMail, &sbuf) == 0)
+ return (NULL);
+ if (!freopen(calendarFile, "r", stdin))
+ return (NULL);
+ } else {
+ chdir(getenv("HOME"));
+ if (!(chdir(calendarHome) == 0 &&
+ freopen(calendarFile, "r", stdin)))
+ errx(1, "no calendar file: ``%s'' or ``~/%s/%s\n", calendarFile, calendarHome, calendarFile);
+ }
+ }
+ 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]);
+ uid = geteuid();
+ if (setuid(getuid()) < 0) {
+ warnx("first setuid failed");
+ _exit(1);
+ };
+ if (setgid(getegid()) < 0) {
+ warnx("setgid failed");
+ _exit(1);
+ }
+ if (setuid(uid) < 0) {
+ warnx("setuid failed");
+ _exit(1);
+ }
+ execl(_PATH_CPP, "cpp", "-P", "-I.", _PATH_INCLUDE, NULL);
+ warn(_PATH_CPP);
+ _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;
+{
+ uid_t uid;
+ 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]);
+ uid = geteuid();
+ if (setuid(getuid()) < 0) {
+ warnx("setuid failed");
+ _exit(1);
+ };
+ if (setgid(getegid()) < 0) {
+ warnx("setgid failed");
+ _exit(1);
+ }
+ if (setuid(uid) < 0) {
+ warnx("setuid failed");
+ _exit(1);
+ }
+ execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
+ "\"Reminder Service\"", NULL);
+ warn(_PATH_SENDMAIL);
+ _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);
+}
diff --git a/usr.bin/calendar/ostern.c b/usr.bin/calendar/ostern.c
new file mode 100644
index 0000000..2527eb0
--- /dev/null
+++ b/usr.bin/calendar/ostern.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "calendar.h"
+
+/* return year day for Easter */
+
+int easter (year)
+ int year; /* 0 ... abcd, NOT since 1900 */
+{
+
+ int e_a, e_b, e_c, e_d, e_e,e_f, e_g, e_h, e_i, e_k,
+ e_l, e_m, e_n, e_p, e_q;
+
+ /* silly, but it works */
+ e_a = year % 19;
+ e_b = year / 100;
+ e_c = year % 100;
+
+ e_d = e_b / 4;
+ e_e = e_b % 4;
+ e_f = (e_b + 8) / 25;
+ e_g = (e_b + 1 - e_f) / 3;
+ e_h = ((19 * e_a) + 15 + e_b - (e_d + e_g)) % 30;
+ e_i = e_c / 4;
+ e_k = e_c % 4;
+ e_l = (32 + 2 * e_e + 2 * e_i - (e_h + e_k)) % 7;
+ e_m = (e_a + 11 * e_h + 22 * e_l) / 451;
+ e_n = (e_h + e_l + 114 - (7 * e_m)) / 31;
+ e_p = (e_h + e_l + 114 - (7 * e_m)) % 31;
+ e_p = e_p + 1;
+
+ e_q = 31 + 28;
+
+ if (e_k == 0 && e_c != 0)
+ e_q += 1;
+
+ if (e_n == 4)
+ e_q += 31;
+
+ e_q += e_p;
+
+#if DEBUG
+ printf("%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", e_a , e_b , e_c , e_d , e_e , e_f , e_g , e_h , e_i , e_k , e_l , e_m , e_n , e_p , e_q);
+#endif
+
+ return (e_q);
+}
+
+/* return year day for Easter or easter depending days
+ * Match: Easter([+-][0-9]+)?
+ * e.g: Easter-2 is Good Friday (2 days before Easter)
+ */
+
+int
+geteaster(s, year)
+ char *s;
+ int year;
+{
+ register int offset = 0;
+ extern struct fixs neaster;
+
+#define EASTER "easter"
+#define EASTERNAMELEN (sizeof(EASTER) - 1)
+
+ if (strncasecmp(s, EASTER, EASTERNAMELEN) == 0)
+ s += EASTERNAMELEN;
+ else if ( neaster.name != NULL
+ && strncasecmp(s, neaster.name, neaster.len) == 0
+ )
+ s += neaster.len;
+ else
+ return(0);
+
+#if DEBUG
+ printf("%s %d %d\n", s, year, EASTERNAMELEN);
+#endif
+
+ /* Easter+1 or Easter-2
+ * ^ ^ */
+
+ switch(*s) {
+
+ case '-':
+ case '+':
+ offset = atoi(s);
+ break;
+
+ default:
+ offset = 0;
+ }
+
+ return (easter(year) + offset);
+}
diff --git a/usr.bin/calendar/paskha.c b/usr.bin/calendar/paskha.c
new file mode 100644
index 0000000..47f686f
--- /dev/null
+++ b/usr.bin/calendar/paskha.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 1993-1996 by Andrey A. Chernov, Moscow, Russia.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "calendar.h"
+
+#define PASKHA "paskha"
+#define PASKHALEN (sizeof(PASKHA) - 1)
+
+/* return year day for Orthodox Easter using Gauss formula */
+/* (old style result) */
+
+static int
+paskha (R)
+int R; /*year*/
+{
+ int a, b, c, d, e;
+ static int x = 15;
+ static int y = 6;
+ extern int *cumdays;
+
+ a = R % 19;
+ b = R % 4;
+ c = R % 7;
+ d = (19*a + x) % 30;
+ e = (2*b + 4*c + 6*d + y) % 7;
+ return (((cumdays[3] + 1) + 22) + (d + e));
+}
+
+/* return year day for Orthodox Easter depending days */
+
+int
+getpaskha(s, year)
+ char *s;
+ int year;
+{
+ int offset;
+ extern struct fixs npaskha;
+
+ if (strncasecmp(s, PASKHA, PASKHALEN) == 0)
+ s += PASKHALEN;
+ else if ( npaskha.name != NULL
+ && strncasecmp(s, npaskha.name, npaskha.len) == 0
+ )
+ s += npaskha.len;
+ else
+ return 0;
+
+
+ /* Paskha+1 or Paskha-2
+ * ^ ^ */
+
+ switch(*s) {
+
+ case '-':
+ case '+':
+ offset = atoi(s);
+ break;
+
+ default:
+ offset = 0;
+ break;
+ }
+
+ return (paskha(year) + offset + 13/* new style */);
+}
diff --git a/usr.bin/calendar/pathnames.h b/usr.bin/calendar/pathnames.h
new file mode 100644
index 0000000..481989c
--- /dev/null
+++ b/usr.bin/calendar/pathnames.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.
+ *
+ * @(#)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..c7e5dec
--- /dev/null
+++ b/usr.bin/cap_mkdb/cap_mkdb.1
@@ -0,0 +1,102 @@
+.\" 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
+.\" $Id: cap_mkdb.1,v 1.4 1997/02/22 19:54:19 peter Exp $
+.\"
+.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 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 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..75ba9b7
--- /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")) != -1) {
+ 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..0dd5055
--- /dev/null
+++ b/usr.bin/chat/Makefile
@@ -0,0 +1,8 @@
+# $Id$
+
+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..48ed7c8
--- /dev/null
+++ b/usr.bin/chat/chat.8
@@ -0,0 +1,315 @@
+.\" -*- nroff -*-
+.\" manual page [] for chat 1.8
+.\" $Id$
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH CHAT 8 "5 May 1995" "Chat Version 1.9"
+.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 -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 -r \fI<report file>
+Set the file for output of the report strings. If you use the keyword
+\fIREPORT\fR, the resulting strings are written to this file. If this
+option is not used and you still use \fIREPORT\fR keywords, the
+\fIstderr\fR file is used for the report strings.
+.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
+.IR syslogd (8).
+Logging is
+done to the \fIlocal2\fR facility at level \fIinfo\fR for verbose tracing
+and level \fIerr\fR for some errors.
+.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 ssword: 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 REPORT STRINGS
+A \fBreport\fR string is similar to the ABORT string. The difference
+is that the strings, and all characters to the next control character
+such as a carriage return, are written to the report file.
+.LP
+The report strings may be used to isolate the transmission rate of the
+modem's connect string and return the value to the chat user. The
+analysis of the report string logic occurs in conjunction with the
+other string processing such as looking for the expect string. The use
+of the same string for a report and abort sequence is probably not
+very useful, however, it is possible.
+.LP
+The report strings to no change the completion code of the program.
+.LP
+These "report" strings may be specified in the script using the \fIREPORT\fR
+sequence. It is written in the script as in the following example:
+.IP
+REPORT CONNECT ABORT BUSY '' ATDT5551212 CONNECT '' ogin: account
+.LP
+This sequence will expect nothing; and then send 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. In addition the program will write to the
+expect-file the string "CONNECT" plus any characters which follow it
+such as the connection rate.
+.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 assword: 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
+.IR syslogd (8).
+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 TERMINATION CODES
+The \fIchat\fR program will terminate with the following completion
+codes.
+.TP
+.B 0
+The normal termination of the program. This indicates that the script
+was executed without error to the normal conclusion.
+.TP
+.B 1
+One or more of the parameters are invalid or an expect string was too
+large for the internal buffers. This indicates that the program as not
+properly executed.
+.TP
+.B 2
+An error occurred during the execution of the program. This may be due
+to a read or write operation failing for some reason or chat receiving
+a signal such as SIGINT.
+.TP
+.B 3
+A timeout event occurred when there was an \fIexpect\fR string without
+having a "-subsend" string. This may mean that you did not program the
+script correctly for the condition or that some unexpected event has
+occurred and the expected string could not be found.
+.TP
+.B 4
+The first string marked as an \fIABORT\fR condition occurred.
+.TP
+.B 5
+The second string marked as an \fIABORT\fR condition occurred.
+.TP
+.B 6
+The third string marked as an \fIABORT\fR condition occurred.
+.TP
+.B 7
+The fourth string marked as an \fIABORT\fR condition occurred.
+.TP
+.B ...
+The other termination codes are also strings marked as an \fIABORT\fR
+condition.
+.LP
+Using the termination code, it is possible to determine which event
+terminated the script. It is possible to decide if the string "BUSY"
+was received from the modem as opposed to "NO DIAL TONE". While the
+first event may be retried, the second will probably have little
+chance of succeeding during a retry.
+.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(1), 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..9f70bc6
--- /dev/null
+++ b/usr.bin/chat/chat.c
@@ -0,0 +1,1348 @@
+/*
+ * Chat -- a program for automatic session establishment (i.e. dial
+ * the phone and log in).
+ *
+ * Standard termination codes:
+ * 0 - successful completion of the script
+ * 1 - invalid argument, expect string too large, etc.
+ * 2 - error on an I/O operation or fatal error condtion.
+ * 3 - timeout waiting for a simple string.
+ * 4 - the first string declared as "ABORT"
+ * 5 - the second string declared as "ABORT"
+ * 6 - ... and so on for successive ABORT strings.
+ *
+ * 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.)
+ *
+ * Added -r "report file" switch & REPORT keyword.
+ * Robert Geer <bgeer@xmission.com>
+ *
+ * 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.7 1997/04/02 09:55:26 jmg Exp $";
+
+#include <stdio.h>
+#include <time.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 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
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#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() *********************************************/
+
+#define MAX_ABORTS 50
+#define MAX_REPORTS 50
+#define DEFAULT_CHAT_TIMEOUT 45
+
+int verbose = 0;
+int quiet = 0;
+int report = 0;
+int exit_code = 0;
+FILE* report_fp = (FILE *) 0;
+char *report_file = (char *) 0;
+char *chat_file = (char *) 0;
+int timeout = DEFAULT_CHAT_TIMEOUT;
+
+int have_tty_parameters = 0;
+
+#ifdef TERMIO
+#define term_parms struct termio
+#define get_term_param(param) ioctl(0, TCGETA, param)
+#define set_term_param(param) ioctl(0, TCSETA, param)
+struct termio saved_tty_parameters;
+#endif
+
+#ifdef TERMIOS
+#define term_parms struct termios
+#define get_term_param(param) tcgetattr(0, param)
+#define set_term_param(param) tcsetattr(0, TCSANOW, param)
+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;
+
+char *report_string[MAX_REPORTS] ;
+char report_buffer[50] ;
+int n_reports = 0, report_next = 0, report_gathering = 0 ;
+
+void *dup_mem __P((void *b, size_t c));
+void *copy_of __P((char *s));
+static 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));
+int get_string __P((register char *string));
+int put_string __P((register char *s));
+int write_char __P((int c));
+int put_char __P((int c));
+int get_char __P((void));
+void chat_send __P((register char *s));
+char *character __P((int c));
+void chat_expect __P((register char *s));
+char *clean __P((register char *s, int sending));
+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 ] [ -f chat-file ] [ -r report-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;
+
+ tzset();
+
+ 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 't':
+ if (arg = OPTARG(argc, argv))
+ {
+ timeout = atoi(arg);
+ }
+ else
+ {
+ usage();
+ }
+ break;
+
+ case 'r':
+ arg = OPTARG (argc, argv);
+ if (arg)
+ {
+ if (report_fp != NULL)
+ {
+ fclose (report_fp);
+ }
+ report_file = copy_of (arg);
+ report_fp = fopen (report_file, "a");
+ if (report_fp != NULL)
+ {
+ if (verbose)
+ {
+ fprintf (report_fp, "Opening \"%s\"...\n",
+ report_file);
+ }
+ report = 1;
+ }
+ }
+ break;
+
+ default:
+ usage();
+ break;
+ }
+ }
+/*
+ * Default the report file to the stderr location
+ */
+ if (report_fp == NULL)
+ {
+ report_fp = stderr;
+ }
+
+#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;
+
+ cfp = fopen (chat_file, "r");
+ if (cfp == 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.
+ */
+static void
+usage()
+ {
+ fprintf(stderr, "%s %s\n",
+ "usage: chat [-v] [-t timeout] [-r report-file]",
+ "{-f chat-file | chat-script}");
+ 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;
+ }
+ }
+
+/*
+ * Terminate with an error.
+ */
+void die()
+ {
+ terminate(1);
+ }
+
+/*
+ * Print an error message and terminate.
+ */
+
+void fatal (msg)
+const char *msg;
+ {
+ syslog(LOG_ERR, "%s", msg);
+ terminate(2);
+ }
+
+/*
+ * Print an error message along with the system error message and
+ * terminate.
+ */
+
+void sysfatal (msg)
+const char *msg;
+ {
+ syslog(LOG_ERR, "%s: %m", msg);
+ terminate(2);
+ }
+
+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 | O_NONBLOCK) == -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 & ~O_NONBLOCK) == -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);
+
+ set_tty_parameters();
+ signal(SIGALRM, sigalrm);
+ alarm(0);
+ alarmed = 0;
+ }
+
+void set_tty_parameters()
+ {
+#if defined(get_term_param)
+ term_parms t;
+
+ if (get_term_param (&t) < 0)
+ {
+ have_tty_parameters = 0;
+ return;
+ }
+
+ 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;
+
+ if (set_term_param (&t) < 0)
+ {
+ sysfatal("Can't set terminal parameters");
+ }
+#endif
+ }
+
+void break_sequence()
+ {
+#ifdef TERMIOS
+ tcsendbreak (0, 0);
+#endif
+ }
+
+void terminate(status)
+int status;
+ {
+ if (report_file != (char *) 0 && report_fp != (FILE *) NULL)
+ {
+ if (verbose)
+ {
+ fprintf (report_fp, "Closing \"%s\".\n", report_file);
+ }
+ fclose (report_fp);
+ report_fp = (FILE*) NULL;
+ }
+
+#if defined(get_term_param)
+ if (have_tty_parameters)
+ {
+ if (set_term_param (&saved_tty_parameters) < 0)
+ {
+ syslog(LOG_ERR, "Can't restore terminal parameters: %m");
+ exit(1);
+ }
+ }
+#endif
+
+ exit(status);
+ }
+
+/*
+ * '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, "REPORT") == 0)
+ {
+ ++report_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");
+ }
+
+ terminate(exit_code);
+ }
+ }
+ }
+ }
+
+char *character(c)
+int 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) + 1 > sizeof(fail_buffer))
+ {
+ 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");
+ }
+ return;
+ }
+
+ if (report_next)
+ {
+ char *s1;
+
+ report_next = 0;
+ if (n_reports >= MAX_REPORTS)
+ {
+ fatal("Too many REPORT strings");
+ }
+
+ s1 = clean(s, 0);
+
+ if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
+ {
+ syslog(LOG_WARNING, "Illegal or too-long REPORT string ('%s')", s);
+ die();
+ }
+
+ report_string[n_reports++] = s1;
+
+ if (verbose)
+ {
+ logf("report (");
+ s1 = s;
+ while (*s1)
+ {
+ logf(character(*s1));
+ ++s1;
+ }
+ logf(")\n");
+ }
+ return;
+ }
+
+ 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);
+ }
+ return;
+ }
+
+ 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");
+ 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 & ~O_NONBLOCK) == -1)
+ {
+ sysfatal("Can't set file mode flags on stdin");
+ }
+ }
+
+ return (-1);
+ }
+ }
+
+int put_char(c)
+int c;
+ {
+ int status;
+ char ch = c;
+
+ usleep(10000); /* inter-character typing delay (?) */
+
+ status = write(1, &ch, 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 & ~O_NONBLOCK) == -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 (arg is microseconds) */
+ 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");
+ exit_code = 1;
+ 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, report_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;
+ exit_code = n + 4;
+ strcpy(fail_reason = fail_buffer, abort_string[n]);
+ return (0);
+ }
+ }
+
+ if (!report_gathering)
+ {
+ for (n = 0; n < n_reports; ++n)
+ {
+ if ((report_string[n] != (char*) NULL) &&
+ s - temp >= (report_len = strlen(report_string[n])) &&
+ strncmp(s - report_len, report_string[n], report_len) == 0)
+ {
+ time_t time_now = time ((time_t*) NULL);
+ struct tm* tm_now = localtime (&time_now);
+
+ strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
+ strcat (report_buffer, report_string[n]);
+
+ report_string[n] = (char *) NULL;
+ report_gathering = 1;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (!iscntrl (c))
+ {
+ int rep_len = strlen (report_buffer);
+ report_buffer[rep_len] = c;
+ report_buffer[rep_len + 1] = '\0';
+ }
+ else
+ {
+ report_gathering = 0;
+ fprintf (report_fp, "chat: %s\n", report_buffer);
+ }
+ }
+
+ 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");
+ }
+ }
+
+ exit_code = 3;
+ alarmed = 0;
+ return (0);
+ }
+
+#ifdef 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
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/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..5cdcc9c
--- /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 me 7 ,
+.Xr ms 7
+.\" .Xr checkeq 1 ,
+.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..f8531cc
--- /dev/null
+++ b/usr.bin/checknr/checknr.c
@@ -0,0 +1,616 @@
+/*
+ * 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 <stdlib.h>
+#include <string.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 */
+
+void addcmd __P((char *));
+void addmac __P((char *));
+int binsrch __P((char *));
+void checkknown __P((char *));
+void chkcmd __P((char *, char *));
+void complain __P((int));
+int eq __P((char *, char *));
+void nomatch __P((char *));
+void pe __P((int));
+void process __P((FILE *));
+void prop __P((int));
+static void usage __P((void));
+
+/*
+ * 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 */
+
+int
+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);
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: checknr [-a.xx.yy.xx.yy...] [-c.xx.xx.xx...] [-s] [-f] file\n");
+ exit(1);
+}
+
+void
+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);
+ }
+}
+
+void
+complain(i)
+int i;
+{
+ pe(stk[i].lno);
+ printf("Unmatched ");
+ prop(i);
+ printf("\n");
+}
+
+void
+prop(i)
+int 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);
+ }
+}
+
+void
+chkcmd(line, mac)
+char *line;
+char *mac;
+{
+ register int i;
+
+ /*
+ * 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;
+ }
+ }
+ }
+}
+
+void
+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? */
+int
+eq(s1, s2)
+char *s1, *s2;
+{
+ return (strcmp(s1, s2) == 0);
+}
+
+/* print the first part of an error message, given the line number */
+void
+pe(lineno)
+int lineno;
+{
+ if (nfiles > 1)
+ printf("%s: ", cfilename);
+ printf("%d: ", lineno);
+}
+
+void
+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.
+ */
+void
+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.
+ */
+void
+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.
+ */
+int
+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..d269255
--- /dev/null
+++ b/usr.bin/chflags/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+NOSHARED?=yes
+
+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..99424f8
--- /dev/null
+++ b/usr.bin/chflags/chflags.1
@@ -0,0 +1,137 @@
+.\" 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
+.\" $Id$
+.\"
+.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)
+sunlnk set the system undeletable 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)
+uunlnk set the user undeletable flag (owner or super-user only)
+archived, sappend, schange, simmutable, uappend, uchange, uimmutable,
+sunlink, uunlink
+ 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
+You can use "ls -lo" to see the flags of existing files.
+.Pp
+The
+.Nm chflags
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr ls 1 ,
+.Xr chflags 2 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Xr symlink 7
+.Sh HISTORY
+The
+.Nm chflags
+command first appeared in
+.Bx 4.4 .
diff --git a/usr.bin/chflags/chflags.c b/usr.bin/chflags/chflags.c
new file mode 100644
index 0000000..f33f46e
--- /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")) != -1)
+ 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/chkey/Makefile b/usr.bin/chkey/Makefile
new file mode 100644
index 0000000..6cc1d1c
--- /dev/null
+++ b/usr.bin/chkey/Makefile
@@ -0,0 +1,14 @@
+# $Id$
+
+PROG= chkey
+SRCS= chkey.c update.c generic.c
+
+.PATH: ${.CURDIR}/../newkey
+
+MAN1= chkey.1
+
+CFLAGS+= -DYP
+
+LDADD+= -lrpcsvc -lmp -lgmp
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/chkey/chkey.1 b/usr.bin/chkey/chkey.1
new file mode 100644
index 0000000..7db240d
--- /dev/null
+++ b/usr.bin/chkey/chkey.1
@@ -0,0 +1,21 @@
+.\" @(#)chkey.1 1.5 91/03/11 TIRPC 1.0;
+.\" Copyright (c) 1988 Sun Microsystems, Inc. - All Rights Reserved.
+.\"
+.TH CHKEY 1 "5 July 1989"
+.SH NAME
+chkey \- change your encryption key
+.SH SYNOPSIS
+.B chkey
+.SH DESCRIPTION
+.LP
+.B chkey
+prompts the user for their login password,
+and uses it to encrypt a new encryption key
+for the user to be stored in the
+.BR publickey (5)
+database.
+.SH "SEE ALSO"
+.BR keylogin (1),
+.BR publickey (5),
+.BR keyserv (8C),
+.BR newkey (8).
diff --git a/usr.bin/chkey/chkey.c b/usr.bin/chkey/chkey.c
new file mode 100644
index 0000000..b769054
--- /dev/null
+++ b/usr.bin/chkey/chkey.c
@@ -0,0 +1,288 @@
+/*
+ * 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 or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * 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[] = "@(#)chkey.c 1.7 91/03/11 Copyr 1986 Sun Micro";
+#endif
+/*
+ * Copyright (C) 1986, Sun Microsystems, Inc.
+ */
+
+/*
+ * Command to change one's public key in the public key database
+ */
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#ifdef YP
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#else
+#define YPOP_STORE 4
+#endif
+#include <pwd.h>
+#include <string.h>
+#include <sys/fcntl.h>
+
+extern char *getpass();
+#define index strchr
+extern char *crypt();
+#ifdef YPPASSWD
+struct passwd *ypgetpwuid();
+#endif
+
+#ifdef YP
+static char *domain;
+static char PKMAP[] = "publickey.byname";
+#else
+static char PKFILE[] = "/etc/publickey";
+#endif /* YP */
+static char ROOTKEY[] = "/etc/.rootkey";
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char name[MAXNETNAMELEN+1];
+ char public[HEXKEYBYTES + 1];
+ char secret[HEXKEYBYTES + 1];
+ char crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE + 1];
+ char crypt2[HEXKEYBYTES + KEYCHECKSUMSIZE + 1];
+ int status;
+ char *pass;
+ struct passwd *pw;
+ uid_t uid;
+ int force = 0;
+ char *self;
+#ifdef YP
+ char *master;
+#endif
+
+ self = argv[0];
+ for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
+ if (argv[0][2] != 0) {
+ usage(self);
+ }
+ switch (argv[0][1]) {
+ case 'f':
+ force = 1;
+ break;
+ default:
+ usage(self);
+ }
+ }
+ if (argc != 0) {
+ usage(self);
+ }
+
+#ifdef YP
+ (void)yp_get_default_domain(&domain);
+ if (yp_master(domain, PKMAP, &master) != 0) {
+ (void)fprintf(stderr,
+ "can't find master of publickey database\n");
+ exit(1);
+ }
+#endif
+ uid = getuid() /*geteuid()*/;
+ if (uid == 0) {
+ if (host2netname(name, NULL, NULL) == 0) {
+ (void)fprintf(stderr,
+ "chkey: cannot convert hostname to netname\n");
+ exit(1);
+ }
+ } else {
+ if (user2netname(name, uid, NULL) == 0) {
+ (void)fprintf(stderr,
+ "chkey: cannot convert username to netname\n");
+ exit(1);
+ }
+ }
+ (void)printf("Generating new key for %s.\n", name);
+
+ if (!force) {
+ if (uid != 0) {
+#ifdef YPPASSWD
+ pw = ypgetpwuid(uid);
+#else
+ pw = getpwuid(uid);
+#endif
+ if (pw == NULL) {
+#ifdef YPPASSWD
+ (void)fprintf(stderr,
+ "No NIS password entry found: can't change key.\n");
+#else
+ (void)fprintf(stderr,
+ "No password entry found: can't change key.\n");
+#endif
+ exit(1);
+ }
+ } else {
+ pw = getpwuid(0);
+ if (pw == NULL) {
+ (void)fprintf(stderr,
+ "No password entry found: can't change key.\n");
+ exit(1);
+ }
+ }
+ }
+ pass = getpass("Password:");
+#ifdef YPPASSWD
+ if (!force) {
+ if (strcmp(crypt(pass, pw->pw_passwd), pw->pw_passwd) != 0) {
+ (void)fprintf(stderr, "Invalid password.\n");
+ exit(1);
+ }
+ }
+#else
+ force = 1; /* Make this mandatory */
+#endif
+ genkeys(public, secret, pass);
+
+ memcpy(crypt1, secret, HEXKEYBYTES);
+ memcpy(crypt1 + HEXKEYBYTES, secret, KEYCHECKSUMSIZE);
+ crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0;
+ xencrypt(crypt1, pass);
+
+ if (force) {
+ memcpy(crypt2, crypt1, HEXKEYBYTES + KEYCHECKSUMSIZE + 1);
+ xdecrypt(crypt2, getpass("Retype password:"));
+ if (memcmp(crypt2, crypt2 + HEXKEYBYTES, KEYCHECKSUMSIZE) != 0 ||
+ memcmp(crypt2, secret, HEXKEYBYTES) != 0) {
+ (void)fprintf(stderr, "Password incorrect.\n");
+ exit(1);
+ }
+ }
+
+#ifdef YP
+ (void)printf("Sending key change request to %s...\n", master);
+#endif
+ status = setpublicmap(name, public, crypt1);
+ if (status != 0) {
+#ifdef YP
+ (void)fprintf(stderr,
+ "%s: unable to update NIS database (%u): %s\n",
+ self, status, yperr_string(status));
+#else
+ (void)fprintf(stderr,
+ "%s: unable to update publickey database\n", self);
+#endif
+ exit(1);
+ }
+
+ if (uid == 0) {
+ /*
+ * Root users store their key in /etc/$ROOTKEY so
+ * that they can auto reboot without having to be
+ * around to type a password. Storing this in a file
+ * is rather dubious: it should really be in the EEPROM
+ * so it does not go over the net.
+ */
+ int fd;
+
+ fd = open(ROOTKEY, O_WRONLY|O_TRUNC|O_CREAT, 0);
+ if (fd < 0) {
+ perror(ROOTKEY);
+ } else {
+ char newline = '\n';
+
+ if (write(fd, secret, strlen(secret)) < 0 ||
+ write(fd, &newline, sizeof(newline)) < 0) {
+ (void)fprintf(stderr, "%s: ", ROOTKEY);
+ perror("write");
+ }
+ }
+ }
+
+ if (key_setsecret(secret) < 0) {
+ (void)printf("Unable to login with new secret key.\n");
+ exit(1);
+ }
+ (void)printf("Done.\n");
+ exit(0);
+ /* NOTREACHED */
+}
+
+usage(name)
+ char *name;
+{
+ (void)fprintf(stderr, "usage: %s [-f]\n", name);
+ exit(1);
+ /* NOTREACHED */
+}
+
+
+/*
+ * Set the entry in the public key file
+ */
+setpublicmap(name, public, secret)
+ char *name;
+ char *public;
+ char *secret;
+{
+ char pkent[1024];
+
+ (void)sprintf(pkent,"%s:%s", public, secret);
+#ifdef YP
+ return (yp_update(domain, PKMAP, YPOP_STORE,
+ name, strlen(name), pkent, strlen(pkent)));
+#else
+ return (localupdate(name, PKFILE, YPOP_STORE,
+ strlen(name), name, strlen(pkent), pkent));
+#endif
+}
+
+#ifdef YPPASSWD
+struct passwd *
+ypgetpwuid(uid)
+ uid_t uid;
+{
+ char uidstr[10];
+ char *val;
+ int vallen;
+ static struct passwd pw;
+ char *p;
+
+ (void)sprintf(uidstr, "%d", uid);
+ if (yp_match(domain, "passwd.byuid", uidstr, strlen(uidstr),
+ &val, &vallen) != 0) {
+ return (NULL);
+ }
+ p = index(val, ':');
+ if (p == NULL) {
+ return (NULL);
+ }
+ pw.pw_passwd = p + 1;
+ p = index(pw.pw_passwd, ':');
+ if (p == NULL) {
+ return (NULL);
+ }
+ *p = 0;
+ return (&pw);
+}
+#endif /* YPPASSWD */
diff --git a/usr.bin/chpass/Makefile b/usr.bin/chpass/Makefile
new file mode 100644
index 0000000..cef2d57
--- /dev/null
+++ b/usr.bin/chpass/Makefile
@@ -0,0 +1,63 @@
+# @(#)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 \
+ ${.CURDIR}/../../libexec/ypxfr \
+ ${.CURDIR}/../../usr.sbin/rpc.yppasswdd
+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 -I. -I${.CURDIR}/../../libexec/ypxfr \
+ -I${.CURDIR}/../../usr.sbin/rpc.yppasswdd -Dyp_error=warnx
+
+#Some people need this, uncomment to activate
+#COPTS+= -DRESTRICT_FULLNAME_CHANGE
+
+SRCS+= yppasswd_private_xdr.c yppasswd_comm.c yp_clnt.c \
+ yppasswd_clnt.c pw_yp.c ypxfr_misc.c
+CLEANFILES= yp_clnt.c yp.h yppasswd_clnt.c yppasswd.h \
+ yppasswd_private_xdr.c yppasswd_private.h
+
+DPADD= ${LIBRPCSVC} ${LIBCRYPT}
+LDADD+= -lrpcsvc -lcrypt
+
+RPCGEN= rpcgen -C
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/yp.x
+RPCSRC_PW= ${DESTDIR}/usr/include/rpcsvc/yppasswd.x
+RPCSRC_PRIV= ${.CURDIR}/../../usr.sbin/rpc.yppasswdd/yppasswd_private.x
+
+yp.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+yp_clnt.c: ${RPCSRC} yp.h
+ ${RPCGEN} -l -o ${.TARGET} ${RPCSRC}
+
+yppasswd.h: ${RPCSRC_PW}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC_PW}
+
+yppasswd_clnt.c: ${RPCSRC_PW} yppasswd.h
+ ${RPCGEN} -l -o ${.TARGET} ${RPCSRC_PW}
+
+yppasswd_private.h: ${RPCSRC_PRIV}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC_PRIV}
+
+yppasswd_private_xdr.c: ${RPCSRC_PRIV} yppasswd_private.h
+ ${RPCGEN} -c -o ${.TARGET} ${RPCSRC_PRIV}
+
+beforeinstall:
+.for i in chpass chfn chsh ypchpass ypchfn ypchsh
+ [ ! -e ${DESTDIR}${BINDIR}/$i ] || \
+ chflags noschg ${DESTDIR}${BINDIR}/$i
+.endfor
+
+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..e9059a6
--- /dev/null
+++ b/usr.bin/chpass/chpass.1
@@ -0,0 +1,422 @@
+.\" 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
+.\" $Id$
+.\"
+.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
+.Nm 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 rpc.yppasswdd 8 ,
+which normally only permits changes to a user's password, shell and GECOS
+fields. Except when invoked by the super-user on the NIS master server,
+.Nm chpass
+(and, similarly,
+.Xr passwd 1 )
+can not use the
+.Xr rpc.yppasswdd 8
+server to change other user information or
+add new records to the NIS passwd maps.
+Furthermore,
+.Xr rpc.yppasswdd 8
+requires password authentication before it will make any
+changes. The only user allowed to submit changes without supplying
+a password is the super-user on the NIS master server; all other users,
+including those with root privileges on NIS clients (and NIS slave
+servers) must enter a password.
+(The super-user on the NIS master is allowed to bypass these restrictions
+largely for convenience: a user with root access
+to the NIS master server already has the privileges required to make
+updates to the NIS maps, but editing the map source files by hand can
+be cumbersome.
+.Pp
+Note: these exceptions only apply when the NIS master server is a
+FreeBSD system.)
+.Pp
+Consequently, except where noted, 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.
+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.
+.Pp
+Exception: the super-user on the NIS master server is permitted to
+change any field.
+.Pp
+.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.
+.Pp
+Exception: the super-user on the NIS master server is allowed to
+submit changes without supplying a password. (The super-user may
+choose to turn off this feature using the
+.Fl o
+flag, described below.)
+.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.
+.Pp
+The super-user on the NIS master server is permitted to add new records
+to the NIS password maps, provided the
+.Xr rpc.yppasswdd 8
+server has been started with the
+.Fl a
+flag to permitted additions (it refuses them by default).
+.Nm Chpass
+tries to update the local password database by default; to update the
+NIS maps instead, invoke chpass with the
+.Fl y
+flag.
+.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 rpc.yppasswdd 8
+will refuse to update the NIS maps.
+.Pp
+Exception: the super-user on the NIS master server is permitted to
+change a user's NIS password with
+.Nm chpass .
+.El
+.Pp
+There are also a few 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.
+.It Fl d Ar domain
+Specify a particular NIS domain.
+.Nm Chpass
+uses the system domain name by default, as set by the
+.Xr domainname 1
+command. The
+.Fl d
+option can be used to override a default, or to specify a domain
+when the system domain name is not set.
+.It Fl h Ar host
+Specify the name or address of an NIS server to query. Normally,
+.Nm chpass
+will communicate with the NIS master host specified in the
+.Pa master.passwd
+or
+.Pa passwd
+maps. On hosts that have not been configured as NIS clients, there is
+no way for the program to determine this information unless the user
+provides the hostname of a server. Note that the specified hostname need
+not be that of the NIS master server; the name of any server, master or
+slave, in a given NIS domain will do.
+.Pp
+When using the
+.Fl d
+option, the hostname defaults to ``localhost.'' The
+.Fl h
+option can be used in conjunction with the
+.Fl d
+option, in which case the user-specified hostname will override
+the default.
+.Pp
+.It Fl o
+Force the use of RPC-based updates when communicating with
+.Xr rpc.yppasswdd 8
+(``old-mode'').
+When invoked by the super-user on the NIS master server,
+.Nm chpass
+allows unrestricted changes to the NIS passwd maps using dedicated,
+non-RPC-based mechanism (in this case, a UNIX domain socket). The
+.Fl o
+flag can be used to force
+.Nm chpass
+to use the standard update mechanism instead. This option is provided
+mainly for testing purposes.
+.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 finger 1 ,
+.Xr login 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 ypchsh 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..982548f
--- /dev/null
+++ b/usr.bin/chpass/chpass.c
@@ -0,0 +1,291 @@
+/*-
+ * 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.13 1997/02/22 19:54:25 peter 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 <rpcsvc/yp.h>
+int yp_errno = YP_TRUE;
+#include "pw_yp.h"
+#endif
+
+#include "chpass.h"
+#include "pathnames.h"
+
+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, NEWEXP } op;
+ struct passwd *pw, lpw;
+ char *username = NULL;
+ 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:e:d:h:oly")) != -1)
+#else
+ while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1)
+#endif
+ switch(ch) {
+ case 'a':
+ op = LOADENTRY;
+ arg = optarg;
+ break;
+ case 's':
+ op = NEWSH;
+ arg = optarg;
+ break;
+ case 'p':
+ op = NEWPW;
+ arg = optarg;
+ break;
+ case 'e':
+ op = NEWEXP;
+ arg = optarg;
+ break;
+#ifdef YP
+ case 'h':
+#ifdef PARANOID
+ if (getuid()) {
+ warnx("Only the superuser can use the -h flag");
+ } else {
+#endif
+ yp_server = optarg;
+#ifdef PARANOID
+ }
+#endif
+ break;
+ case 'd':
+#ifdef PARANOID
+ if (getuid()) {
+ warnx("Only the superuser can use the -d flag");
+ } else {
+#endif
+ yp_domain = optarg;
+ if (yp_server == NULL)
+ yp_server = "localhost";
+#ifdef PARANOID
+ }
+#endif
+ break;
+ case 'l':
+ _use_yp = 0;
+ force_local = 1;
+ break;
+ case 'y':
+ _use_yp = force_yp = 1;
+ break;
+ case 'o':
+ force_old++;
+ break;
+#endif
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ uid = getuid();
+
+ if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP)
+ switch(argc) {
+#ifdef YP
+ case 0:
+ GETPWUID(uid)
+ get_yp_master(1); /* XXX just to set the suser flag */
+ break;
+ case 1:
+ GETPWNAM(*argv)
+ get_yp_master(1); /* XXX just to set the suser flag */
+#else
+ 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);
+#endif
+ 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 == NEWEXP) {
+ if (uid) /* only root can change expire */
+ baduser();
+ if (p_expire(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);
+ }
+ username = pw->pw_name;
+
+ if (op == NEWPW) {
+ if (uid)
+ baduser();
+
+ if(strchr(arg, ':')) {
+ errx(1, "invalid format for password");
+ }
+ pw->pw_passwd = arg;
+ }
+
+ /*
+ * 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(username))
+ 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] [-d domain [-h host]] [-a list] [-p encpass] [-s shell] [-e mmm dd yy] [user]\n");
+#else
+ "usage: chpass [-a list] [-p encpass] [-s shell] [-e mmm dd yy] [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..ad99822
--- /dev/null
+++ b/usr.bin/chpass/edit.c
@@ -0,0 +1,254 @@
+/*-
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 || suser_override)) {
+#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. */
+#ifdef 0
+ else if (ok_shell(pw->pw_shell))
+ /*
+ * Make shell a restricted field. Ugly with a
+ * necklace, but there's not much else to do.
+ */
+#else
+ else if ((!list[E_SHELL].restricted && ok_shell(pw->pw_shell)) || !uid)
+ /*
+ * If change not restrict (table.c) and standard shell
+ * OR if root, then allow editing of shell.
+ */
+#endif
+ (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, line;
+ static 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;
+ }
+ line = 0;
+ while (fgets(buf, sizeof(buf), fp)) {
+ line++;
+ if (!buf[0] || buf[0] == '#')
+ continue;
+ if (!(p = strchr(buf, '\n'))) {
+ warnx("line %d too long", line);
+ goto bad;
+ }
+ *p = '\0';
+ for (ep = list;; ++ep) {
+ if (!ep->prompt) {
+ warnx("unrecognized field on line %d", line);
+ 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 %d corrupted", line);
+ 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);
+
+ while ((len = strlen(pw->pw_gecos)) && pw->pw_gecos[len - 1] == ',')
+ pw->pw_gecos[len - 1] = '\0';
+
+ 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");
+ free(p);
+ return (0);
+ }
+ free(p);
+ 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..73fa479
--- /dev/null
+++ b/usr.bin/chpass/field.c
@@ -0,0 +1,287 @@
+/*
+ * 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 <sys/stat.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();
+ struct stat sbuf;
+
+ 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);
+ }
+ if (stat(pw->pw_shell, &sbuf) < 0) {
+ if (errno == ENOENT)
+ warnx("WARNING: shell '%s' does not exist",
+ pw->pw_shell);
+ else
+ warn("WARNING: can't stat shell '%s'", pw->pw_shell);
+ return (0);
+ }
+ if (!S_ISREG(sbuf.st_mode)) {
+ warnx("WARNING: shell '%s' is not a regular file",
+ pw->pw_shell);
+ return (0);
+ }
+ if ((sbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) == 0) {
+ warnx("WARNING: shell '%s' is not executable", pw->pw_shell);
+ return (0);
+ }
+ 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..fa77f05
--- /dev/null
+++ b/usr.bin/chpass/pw_copy.c
@@ -0,0 +1,132 @@
+/*-
+ * 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];
+ char uidstr[20];
+ char gidstr[20];
+ char chgstr[20];
+ char expstr[20];
+
+ snprintf(uidstr, sizeof(uidstr), "%d", pw->pw_uid);
+ snprintf(gidstr, sizeof(gidstr), "%d", pw->pw_gid);
+ snprintf(chgstr, sizeof(chgstr), "%ld", pw->pw_change);
+ snprintf(expstr, sizeof(expstr), "%ld", pw->pw_expire);
+
+ 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:%s:%s:%s:%s:%s:%s:%s:%s\n",
+ pw->pw_name, pw->pw_passwd,
+ pw->pw_fields & _PWF_UID ? uidstr : "",
+ pw->pw_fields & _PWF_GID ? gidstr : "",
+ pw->pw_class,
+ pw->pw_fields & _PWF_CHANGE ? chgstr : "",
+ pw->pw_fields & _PWF_EXPIRE ? expstr : "",
+ 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:%s:%s:%s:%s:%s:%s:%s:%s\n",
+ pw->pw_name, pw->pw_passwd,
+ pw->pw_fields & _PWF_UID ? uidstr : "",
+ pw->pw_fields & _PWF_GID ? gidstr : "",
+ pw->pw_class,
+ pw->pw_fields & _PWF_CHANGE ? chgstr : "",
+ pw->pw_fields & _PWF_EXPIRE ? expstr : "",
+ 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..0080e85
--- /dev/null
+++ b/usr.bin/chpass/pw_yp.c
@@ -0,0 +1,521 @@
+/*
+ * 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 Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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.9 1997/02/22 19:54:26 peter Exp $
+ */
+
+#ifdef YP
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.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.h>
+struct dom_binding {};
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/yppasswd.h>
+#include <pw_util.h>
+#include "pw_yp.h"
+#include "ypxfr_extern.h"
+#include "yppasswd_comm.h"
+#include "yppasswd_private.h"
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+static HASHINFO openinfo = {
+ 4096, /* bsize */
+ 32, /* ffactor */
+ 256, /* nelem */
+ 2048 * 1024, /* cachesize */
+ NULL, /* hash */
+ 0, /* lorder */
+};
+
+int force_old = 0;
+int _use_yp = 0;
+int suser_override = 0;
+int yp_in_pw_file = 0;
+char *yp_domain = NULL;
+char *yp_server = NULL;
+
+extern char *tempname;
+
+/* 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;
+ static char *buf;
+
+ yp_password.pw_fields = 0;
+
+ buf = (char *)realloc(buf, m + 10);
+ bzero(buf, m + 10);
+
+ /* Turn all colons into NULLs */
+ while (strchr(s, ':')) {
+ s = (strchr(s, ':') + 1);
+ *(s - 1)= '\0';
+ }
+
+ t = buf;
+#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_fields |= _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;
+ static char *buf;
+
+ buf = (char *)realloc(buf, m + 10);
+ bzero(buf, m + 10);
+
+ t = buf;
+ 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;
+}
+
+/*
+ * It is not mandatory that an NIS master server also be a client.
+ * However, if the NIS master is not configured as a client, then the
+ * domain name will not be set and ypbind will not be running, so we
+ * will be unable to use the ypclnt routines inside libc. We therefore
+ * need our own magic version of yp_match() which we can use in any
+ * environment.
+ */
+static int my_yp_match(server, domain, map, key, keylen, result, resultlen)
+ char *server;
+ char *domain;
+ char *map;
+ char *key;
+ unsigned long keylen;
+ char **result;
+ unsigned long *resultlen;
+{
+ ypreq_key ypkey;
+ ypresp_val *ypval;
+ CLIENT *clnt;
+ static char buf[YPMAXRECORD + 2];
+
+ bzero((char *)buf, sizeof(buf));
+
+ if ((clnt = clnt_create(server, YPPROG,YPVERS,"udp")) == NULL) {
+ warnx("failed to create UDP handle: %s",
+ clnt_spcreateerror(server));
+ pw_error(tempname, 0, 1);
+ }
+
+ ypkey.domain = domain;
+ ypkey.map = map;
+ ypkey.key.keydat_len = keylen;
+ ypkey.key.keydat_val = key;
+
+ if ((ypval = ypproc_match_2(&ypkey, clnt)) == NULL) {
+ clnt_destroy(clnt);
+ warnx("%s",clnt_sperror(clnt,"YPPROC_MATCH failed"));
+ pw_error(tempname, 0, 1);
+ }
+
+ clnt_destroy(clnt);
+
+ if (ypval->stat != YP_TRUE) {
+ int stat = ypval->stat;
+ xdr_free(xdr_ypresp_val, (char *)ypval);
+ if (stat == YP_NOMAP && strstr(map, "master.passwd"))
+ return(1);
+ if (stat == YP_NOKEY)
+ return(1);
+ warnx("ypmatch failed: %s", yperr_string(ypprot_err(stat)));
+ pw_error(tempname, 0, 1);
+ }
+
+
+ strncpy((char *)&buf, ypval->val.valdat_val, ypval->val.valdat_len);
+
+ *result = (char *)&buf;
+ *resultlen = ypval->val.valdat_len;
+
+ xdr_free(xdr_ypresp_val, (char *)ypval);
+
+ return(0);
+}
+
+/*
+ * Check if the user we're working with is local or in NIS.
+ */
+int use_yp (user, uid, which)
+ char *user;
+ uid_t uid;
+ int which; /* 0 = use username, 1 = use uid */
+{
+ int user_local = 0, user_yp = 0, user_exists = 0;
+ DB *dbp;
+ DBT key,data;
+ char bf[UT_NAMESIZE + 2];
+ char *result;
+ char *server;
+ int resultlen;
+ char ubuf[UT_NAMESIZE + 2];
+
+ if (which) {
+ snprintf(ubuf, sizeof(ubuf), "%lu", uid);
+ user = (char *)&ubuf;
+ }
+
+ /* Grope around for the user in the usual way */
+ if (which) {
+ if (getpwuid(uid) != NULL)
+ user_exists = 1;
+ } else {
+ if (getpwnam(user) != NULL)
+ user_exists = 1;
+ }
+
+ /* Now grope directly through the user database */
+ if ((dbp = dbopen(_PATH_SMP_DB, O_RDONLY, PERM_SECURE,
+ DB_HASH, &openinfo)) == NULL) {
+ warn("error opening database: %s.", _PATH_MP_DB);
+ pw_error(tempname, 0, 1);
+ }
+
+ /* Is NIS turned on */
+ bf[0] = _PW_KEYYPENABLED;
+ key.data = (u_char *)bf;
+ key.size = 1;
+ yp_in_pw_file = !(dbp->get)(dbp,&key,&data,0);
+ if (_yp_check(NULL) || (yp_domain && yp_server)) {
+ server = get_yp_master(0);
+
+ /* Is the user in the NIS passwd map */
+ if (!my_yp_match(server, yp_domain, which ? "passwd.byuid" :
+ "passwd.byname", user, strlen(user),
+ &result, &resultlen)) {
+ user_yp = user_exists = 1;
+ *(char *)(result + resultlen) = '\0';
+ copy_yp_pass(result, 0, resultlen);
+ }
+ /* Is the user in the NIS master.passwd map */
+ if (user_yp && !my_yp_match(server, yp_domain, which ?
+ "master.passwd.byuid" : "master.passwd.byname",
+ user, strlen(user),
+ &result, &resultlen)) {
+ *(char *)(result + resultlen) = '\0';
+ copy_yp_pass(result, 1, resultlen);
+ }
+ }
+
+ /* Is the user in the local password database */
+
+ bf[0] = which ? _PW_KEYBYUID : _PW_KEYBYNAME;
+ if (which)
+ bcopy((char *)&uid, bf + 1, sizeof(uid));
+ else
+ bcopy((char *)user, bf + 1, MIN(strlen(user), UT_NAMESIZE));
+ key.data = (u_char *)bf;
+ key.size = which ? sizeof(uid) + 1 : 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.
+ */
+char *get_yp_master(getserver)
+ int getserver;
+{
+ char *mastername;
+ int rval, localport;
+ struct stat st;
+
+ /*
+ * Sometimes we are called just to probe for rpc.yppasswdd and
+ * set the suser_override flag. Just return NULL and leave
+ * suser_override at 0 if _use_yp doesn't indicate that NIS is
+ * in use and we weren't called from use_yp() itself.
+ * Without this check, we might try probing and fail with an NIS
+ * error in non-NIS environments.
+ */
+ if ((_use_yp == USER_UNKNOWN || _use_yp == USER_LOCAL_ONLY) &&
+ getserver)
+ return(NULL);
+
+ /* Get default NIS domain. */
+
+ if (yp_domain == NULL && (rval = yp_get_default_domain(&yp_domain))) {
+ warnx("can't get local NIS domain name: %s",yperr_string(rval));
+ pw_error(tempname, 0, 1);
+ }
+
+ /* Get master server of passwd map. */
+
+ if ((mastername = ypxfr_get_master(yp_domain, "passwd.byname",
+ yp_server, yp_server ? 0 : 1)) == NULL) {
+ warnx("can't get name of master NIS server");
+ pw_error(tempname, 0, 1);
+ }
+
+ if (!getserver)
+ return(mastername);
+
+ /* Check if yppasswdd is out there. */
+
+ if ((rval = getrpcport(mastername, YPPASSWDPROG, YPPASSWDPROC_UPDATE,
+ IPPROTO_UDP)) == 0) {
+ warnx("rpc.yppasswdd is not running on the NIS master server");
+ pw_error(tempname, 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("rpc.yppasswdd server not running on reserved port");
+ pw_error(tempname, 0, 1);
+ }
+
+ /* See if _we_ are the master server. */
+ if (!force_old && !getuid() && (localport = getrpcport("localhost",
+ YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP)) != 0) {
+ if (localport == rval && stat(sockname, &st) != -1) {
+ suser_override = 1;
+ mastername = "localhost";
+ }
+ }
+
+ /* 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 rpc.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.) The FreeBSD
+ * rpc.yppasswdd has some special hooks to allow the superuser update
+ * information without specifying a password, however this only works
+ * for the superuser on the NIS master server.
+ */
+void yp_submit(pw)
+ struct passwd *pw;
+{
+ struct yppasswd yppasswd;
+ struct master_yppasswd master_yppasswd;
+ CLIENT *clnt;
+ char *master, *password;
+ int *status = NULL;
+ struct rpc_err err;
+
+ _use_yp = 1;
+
+ /* Get NIS master server name */
+
+ master = get_yp_master(1);
+
+ /* Populate the yppasswd structure that gets handed to yppasswdd. */
+
+ if (suser_override) {
+ master_yppasswd.newpw.pw_passwd = strdup(pw->pw_passwd);
+ master_yppasswd.newpw.pw_name = strdup(pw->pw_name);
+ master_yppasswd.newpw.pw_uid = pw->pw_uid;
+ master_yppasswd.newpw.pw_gid = pw->pw_gid;
+ master_yppasswd.newpw.pw_expire = pw->pw_expire;
+ master_yppasswd.newpw.pw_change = pw->pw_change;
+ master_yppasswd.newpw.pw_fields = pw->pw_fields;
+ master_yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos);
+ master_yppasswd.newpw.pw_dir = strdup(pw->pw_dir);
+ master_yppasswd.newpw.pw_shell = strdup(pw->pw_shell);
+ master_yppasswd.newpw.pw_class = pw->pw_class != NULL ?
+ strdup(pw->pw_class) : "";
+ master_yppasswd.oldpass = ""; /* not really needed */
+ master_yppasswd.domain = yp_domain;
+ } else {
+ 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);
+ yppasswd.oldpass = "";
+ }
+
+ /* Get the user's password for authentication purposes. */
+
+ printf ("Changing NIS information for %s on %s\n",
+ pw->pw_name, master);
+
+ if (pw->pw_passwd[0] && !suser_override) {
+ password = getpass("Please enter password: ");
+ if (strncmp(crypt(password,pw->pw_passwd),
+ pw->pw_passwd,strlen(pw->pw_passwd))) {
+ warnx("Password incorrect.");
+ pw_error(tempname, 0, 1);
+ }
+ yppasswd.oldpass = password; /* XXX */
+ }
+
+ if (suser_override) {
+ /* Talk to server via AF_UNIX socket. */
+ if (senddat(&master_yppasswd)) {
+ warnx("failed to contact local rpc.yppasswdd");
+ pw_error(tempname, 0, 1);
+ }
+ /* Get return code. */
+ status = getresp();
+ } else {
+ /* Create a handle to yppasswdd. */
+
+ if ((clnt = clnt_create(master, YPPASSWDPROG,
+ YPPASSWDVERS, "udp")) == NULL) {
+ warnx("failed to contact rpc.yppasswdd: %s",
+ master, clnt_spcreateerror(master));
+ pw_error(tempname, 0, 1);
+ }
+
+ clnt->cl_auth = authunix_create_default();
+
+ status = yppasswdproc_update_1(&yppasswd, clnt);
+
+ clnt_geterr(clnt, &err);
+
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ }
+
+ /* Call failed: signal the error. */
+
+ if ((!suser_override && err.re_status) != RPC_SUCCESS || status == NULL || *status) {
+ warnx("NIS update failed: %s", (err.re_status != RPC_SUCCESS &&
+ !suser_override) ? clnt_sperrno(err.re_status) :
+ "rpc.yppasswdd returned error status");
+ pw_error(NULL, 0, 1);
+ }
+
+ /* Success. */
+
+ if (suser_override)
+ warnx("NIS information changed on host %s, domain %s",
+ master, yp_domain);
+ else
+ 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..0a1e1c0
--- /dev/null
+++ b/usr.bin/chpass/pw_yp.h
@@ -0,0 +1,126 @@
+/*
+ * 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$
+ */
+
+#ifdef YP
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpc/auth.h>
+#include <rpc/auth_unix.h>
+/* 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 force_old;
+extern int _use_yp;
+extern int suser_override;
+extern struct passwd local_password;
+extern struct passwd yp_password;
+extern void copy_yp_pass __P(( char *, int, int ));
+extern char *yp_domain;
+extern char *yp_server;
+extern void yp_submit __P(( struct passwd * ));
+extern int use_yp __P(( char * , uid_t , int ));
+extern char *get_yp_master __P(( int ));
+extern int yp_in_pw_file;
+
+/*
+ * Yucky.
+ */
+#define GETPWUID(X) \
+ _use_yp = use_yp(NULL, X, 1); \
+ \
+ if (_use_yp == USER_UNKNOWN) { \
+ errx(1, "unknown user: uid %u", X); \
+ } \
+ \
+ if (_use_yp == USER_YP_ONLY) { \
+ if (!force_local) { \
+ _use_yp = 1; \
+ pw = (struct passwd *)&yp_password; \
+ } else \
+ errx(1, "unknown local user: uid %u", X); \
+ } else if (_use_yp == USER_LOCAL_ONLY) { \
+ if (!force_yp) { \
+ _use_yp = 0; \
+ pw = (struct passwd *)&local_password; \
+ } else \
+ errx(1, "unknown NIS user: uid %u", X); \
+ } else if (_use_yp == USER_YP_AND_LOCAL) { \
+ if (!force_local && (force_yp || yp_in_pw_file)) { \
+ _use_yp = 1; \
+ pw = (struct passwd *)&yp_password; \
+ } else { \
+ _use_yp = 0; \
+ pw = (struct passwd *)&local_password; \
+ } \
+ }
+
+#define GETPWNAM(X) \
+ _use_yp = use_yp(X, 0, 0); \
+ \
+ if (_use_yp == USER_UNKNOWN) { \
+ errx(1, "unknown user: %s", X); \
+ } \
+ \
+ if (_use_yp == USER_YP_ONLY) { \
+ if (!force_local) { \
+ _use_yp = 1; \
+ pw = (struct passwd *)&yp_password; \
+ } else \
+ errx(1, "unknown local user: %s.", X); \
+ } 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.", X); \
+ } else if (_use_yp == USER_YP_AND_LOCAL) { \
+ if (!force_local && (force_yp || yp_in_pw_file)) { \
+ _use_yp = 1; \
+ pw = (struct passwd *)&yp_password; \
+ } else { \
+ _use_yp = 0; \
+ pw = (struct passwd *)&local_password; \
+ } \
+ }
+
+#endif /* YP */
diff --git a/usr.bin/chpass/table.c b/usr.bin/chpass/table.c
new file mode 100644
index 0000000..3363e1d
--- /dev/null
+++ b/usr.bin/chpass/table.c
@@ -0,0 +1,64 @@
+/*-
+ * 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, },
+#ifdef RESTRICT_FULLNAME_CHANGE /* do not allow fullname changes */
+ { "full name", p_gecos, 1, 9, e2, },
+#else
+ { "full name", p_gecos, 0, 9, e2, },
+#endif
+ { "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..605a484
--- /dev/null
+++ b/usr.bin/chpass/util.c
@@ -0,0 +1,146 @@
+/*-
+ * 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;
+ if (isdigit(*t)) {
+ month = atoi(t);
+ } else {
+ 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..ee680bec
--- /dev/null
+++ b/usr.bin/cksum/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.2 (Berkeley) 4/28/95
+
+PROG= cksum
+SRCS= cksum.c crc.c print.c sum1.c sum2.c
+LINKS= ${BINDIR}/cksum ${BINDIR}/sum
+MLINKS= cksum.1 sum.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/cksum/cksum.1 b/usr.bin/cksum/cksum.1
new file mode 100644
index 0000000..c94fceb
--- /dev/null
+++ b/usr.bin/cksum/cksum.1
@@ -0,0 +1,180 @@
+.\" 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.2 (Berkeley) 4/28/95
+.\" $Id: cksum.1,v 1.4 1997/06/25 07:02:00 charnier Exp $
+.\"
+.Dd April 28, 1995
+.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 Ar \&1 No \&| Ar \&2
+.Op Ar file ...
+.Nm sum
+.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
+.Nm sum
+utility is identical to the
+.Nm cksum
+utility, except that it defaults to using historic algorithm 1, as
+described below.
+It is provided for compatibility only.
+.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
+and
+.Nm sum
+utilities exit 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr md5 1
+.Rs
+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 conform to
+.St -p1003.2-92 .
+.Sh HISTORY
+The
+.Nm cksum
+utility appeared in
+.Bx 4.4 .
diff --git a/usr.bin/cksum/cksum.c b/usr.bin/cksum/cksum.c
new file mode 100644
index 0000000..1917558
--- /dev/null
+++ b/usr.bin/cksum/cksum.c
@@ -0,0 +1,138 @@
+/*-
+ * 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 const char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)cksum.c 8.2 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register int ch, fd, rval;
+ u_long len, val;
+ char *fn, *p;
+ int (*cfncn) __P((int, unsigned long *, unsigned long *));
+ void (*pfncn) __P((char *, unsigned long, unsigned long));
+
+ if ((p = rindex(argv[0], '/')) == NULL)
+ p = argv[0];
+ else
+ ++p;
+ if (!strcmp(p, "sum")) {
+ cfncn = csum1;
+ pfncn = psum1;
+ } else {
+ cfncn = crc;
+ pfncn = pcrc;
+ }
+
+ while ((ch = getopt(argc, argv, "o:")) != -1)
+ switch (ch) {
+ case 'o':
+ if (!strcmp(optarg, "1")) {
+ cfncn = csum1;
+ pfncn = psum1;
+ } else if (!strcmp(optarg, "2")) {
+ cfncn = csum2;
+ pfncn = psum2;
+ } else {
+ warnx("illegal argument to -o option");
+ 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) {
+ warn("%s", fn);
+ rval = 1;
+ continue;
+ }
+ }
+ if (cfncn(fd, &val, &len)) {
+ warn("%s", fn ? fn : "stdin");
+ rval = 1;
+ } else
+ pfncn(fn, val, len);
+ (void)close(fd);
+ } while (*argv);
+ exit(rval);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: cksum [-o 1 | 2] [file ...]\n");
+ (void)fprintf(stderr, " sum [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..637b1ce
--- /dev/null
+++ b/usr.bin/cmp/cmp.1
@@ -0,0 +1,115 @@
+.\" 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
+.\" $Id: cmp.1,v 1.4 1997/02/22 19:54:29 peter Exp $
+.\"
+.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
+.Nm cmp
+utility compares two files of any type and writes the results
+to the standard output.
+By default,
+.Nm cmp
+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.
+.Sh HISTORY
+A
+.Nm cmp
+command appeared in
+.At v1 .
diff --git a/usr.bin/cmp/cmp.c b/usr.bin/cmp/cmp.c
new file mode 100644
index 0000000..6755ff6
--- /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")) != -1)
+ 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..f540ab4
--- /dev/null
+++ b/usr.bin/cmp/regular.c
@@ -0,0 +1,111 @@
+/*-
+ * 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 <unistd.h>
+
+#include "extern.h"
+
+#define ROUNDPAGE(i) ((i) & ~pagemask)
+
+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;
+ off_t pagemask, off1, off2;
+
+ if (sflag && len1 != len2)
+ exit(1);
+
+ if (skip1 > len1)
+ eofmsg(file1);
+ len1 -= skip1;
+ if (skip2 > len2)
+ eofmsg(file2);
+ len2 -= skip2;
+
+ pagemask = (off_t)getpagesize() - 1;
+ off1 = ROUNDPAGE(skip1);
+ off2 = ROUNDPAGE(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, MAP_SHARED, fd1, off1)) == (u_char *)MAP_FAILED)
+ err(ERR_EXIT, "%s", file1);
+
+ madvise(p1, length, MADV_SEQUENTIAL);
+ if ((p2 = (u_char *)mmap(NULL,
+ (size_t)length, PROT_READ, MAP_SHARED, fd2, off2)) == (u_char *)MAP_FAILED)
+ err(ERR_EXIT, "%s", file2);
+ madvise(p2, length, MADV_SEQUENTIAL);
+
+ dfound = 0;
+ p1 += skip1 - off1;
+ p2 += skip2 - off2;
+ 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..0a15fa1
--- /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);
+
+ dfound = 0;
+ while (skip1--)
+ if (getc(fp1) == EOF)
+ goto eof;
+ while (skip2--)
+ if (getc(fp2) == EOF)
+ goto eof;
+
+ 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..99ecda5
--- /dev/null
+++ b/usr.bin/col/col.1
@@ -0,0 +1,127 @@
+.\" 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
+.At v6 .
diff --git a/usr.bin/col/col.c b/usr.bin/col/col.c
new file mode 100644
index 0000000..88d024e
--- /dev/null
+++ b/usr.bin/col/col.c
@@ -0,0 +1,551 @@
+/*-
+ * 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.5 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <locale.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;
+
+ (void) setlocale(LC_CTYPE, "");
+
+ max_bufd_lines = 128;
+ compress_spaces = 1; /* compress spaces into tabs */
+ while ((opt = getopt(argc, argv, "bfhl:x")) != -1)
+ 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++;
+ }
+ if (max_line == 0)
+ exit(0); /* no lines, so just exit */
+
+ /* 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..8c4a88a
--- /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 col 1 ,
+.Xr more 1 ,
+.Xr nroff 1 ,
+.Xr troff 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..f3661a8
--- /dev/null
+++ b/usr.bin/colcrt/colcrt.c
@@ -0,0 +1,257 @@
+/*
+ * 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;
+
+FILE *f;
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register c;
+ register char *cp, *dp;
+
+ argc--;
+ argv++;
+ while (argc > 0 && argv[0][0] == '-') {
+ switch (argv[0][1]) {
+ case 0:
+ suppresul = 1;
+ break;
+ case '2':
+ printall = 1;
+ break;
+ default:
+ usage();
+ }
+ argc--;
+ argv++;
+ }
+ do {
+ if (argc > 0) {
+ close(0);
+ if (!(f = fopen(argv[0], "r"))) {
+ fflush(stdout);
+ err(1, argv[0]);
+ }
+ 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);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: colcrt [ - ] [ -2 ] [ file ... ]\n");
+ exit(1);
+}
+
+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..295fe3f
--- /dev/null
+++ b/usr.bin/colldef/Makefile
@@ -0,0 +1,13 @@
+# $Id$
+
+PROG = colldef
+LFLAGS = -8 -i
+YFLAGS = -d
+CFLAGS += -I. -I${.CURDIR}/../../lib/libc/locale -DCOLLATE_DEBUG
+SRCS = parse.c scan.c
+LDADD = -ll
+DPADD = ${LIBL}
+CLEANFILES += lex.yy.c parse.c scan.c y.tab.[ch]
+SUBDIR = data
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/colldef/colldef.1 b/usr.bin/colldef/colldef.1
new file mode 100644
index 0000000..b668d1a
--- /dev/null
+++ b/usr.bin/colldef/colldef.1
@@ -0,0 +1,252 @@
+.\" 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$
+.\"
+.Dd January, 27 1995
+.Dt COLLDEF 1
+.Os
+.Sh NAME
+.Nm colldef
+.Nd convert collation sequence source definition
+.Sh SYNOPSIS
+.Nm colldef
+.Op Fl I Ar map_dir
+.Op Fl o Ar out_file
+.Op Ar filename
+.Sh DESCRIPTION
+.Nm 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
+.Nm 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
+Options list:
+.Bl -tag -width 4n
+.It Cm Fl I Ar map_dir
+This option set directory name where
+.Ar charmap
+files can be found, current directory by default.
+.It Cm Fl o Ar out_file
+This option set output file name,
+.Ar LC_COLLATE
+by default.
+.El
+.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 .
+.Pp
+Of these, only the
+.Ar order
+statement is required. When
+.Ar charmap
+or
+.Ar 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
+.Ar #
+are
+treated as comments and are ignored. Blank lines are also
+ignored.
+.Pp
+.Ar charmap charmapfile
+.Pp
+.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\fI??\fR) or octal (\e\fI???\fR)
+representation, and can be only one character in length.
+.Pp
+.Ar symbol-name1 symbol-value1
+.br
+.Ar symbol-name2 symbol-value2
+.br
+.Ar ...
+.Pp
+Symbol names cannot be specified in
+.Ar substitute
+fields.
+.Pp
+The
+.Ar charmap
+statement is optional.
+.Pp
+.Ar substitute
+"\fIchar\fR"
+.Ar with
+"\fIrepl\fR"
+.Pp
+The
+.Ar substitute
+statement substitutes the character
+.Ar char
+with the string
+.Ar repl .
+.Pp
+The
+.Ar substitute
+statement is optional.
+.Pp
+.Ar order order_list
+.Pp
+.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
+An order list element
+can be represented in any one of the following
+ways:
+.Bl -bullet
+.It
+The symbol itself (for example,
+.Ar a
+for the lower-case letter
+.Ar a )
+.It
+The symbol chain (for example,
+.Ar abc )
+.It
+In octal representation (for example,
+.Ar \e141
+for the letter
+.Ar a )
+.It
+In hexadecimal representation (for example,
+.Ar \ex61
+for the letter
+.Ar a )
+.It
+The symbol name as defined in the
+.Ar charmap
+file (for example,
+.Ar <abc>
+for
+.Ar abc \e023
+record in
+.Ar charmapfile ) .
+If character map name have
+.Ar >
+character, it must be escaped as
+.Ar /> ,
+single
+.Ar /
+must be escaped as
+.Ar // .
+.It
+Symbols
+.Ar \ea ,
+.Ar \eb ,
+.Ar \ef ,
+.Ar \en ,
+.Ar \er ,
+.Ar \ev
+are permitted in its usual C-language meaning
+.It
+The symbol range (for example,
+.Ar a;...;z )
+.It
+Comma-separated symbols, ranges and chains enclosed in parenthesis (for example
+.Ar \&(
+.Ar sym1 ,
+.Ar sym2 ,
+.Ar ...
+.Ar \&) )
+are assigned the
+same primary ordering but different secondary
+ordering.
+.It
+Comma-separated symbols, ranges and chains enclosed in curly brackets (for example
+.Ar \&{
+.Ar sym1 ,
+.Ar sym2 ,
+.Ar ...
+.Ar \&} )
+are assigned the same primary ordering only.
+.El
+.Pp
+The backslash character
+.Ar \e
+is used for continuation. In this case, no characters are permitted
+after the backslash character.
+.Sh EXIT STATUS
+.Nm 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 setlocale 3 ,
+.Xr strcoll 3 ,
+.Xr strxfrm 3
diff --git a/usr.bin/colldef/data/Makefile b/usr.bin/colldef/data/Makefile
new file mode 100644
index 0000000..e4b6042
--- /dev/null
+++ b/usr.bin/colldef/data/Makefile
@@ -0,0 +1,72 @@
+# $Id: Makefile,v 1.8 1997/03/01 00:28:05 wosch Exp $
+
+NOMAN=YES
+CLEANFILES+= ${LOCALES:S/$/.out/g}
+
+LOCALES= de_DE.ISO_8859-1 \
+ es_ES.ISO_8859-1 \
+ is_IS.ISO_8859-1 \
+ lt_LN.ASCII \
+ lt_LN.ISO_8859-1 \
+ lt_LN.ISO_8859-2 \
+ ru_SU.CP866 \
+ ru_SU.KOI8-R
+
+LOCALEDIR= ${DESTDIR}/usr/share/locale
+
+.if exists(${.OBJDIR}/../colldef)
+COLLDEF=${.OBJDIR}/../colldef
+.else
+COLLDEF=${.CURDIR}/../colldef
+.endif
+
+ASCIILINKS = \
+ ko_KR.EUC ja_JP.EUC
+
+LATIN1LINKS = \
+ da_DK en_AU en_CA en_GB en_US fi_FI \
+ fr_BE fr_CA fr_CH fr_FR it_CH it_IT nl_BE nl_NL no_NO \
+ pt_PT sv_SE
+
+LATIN2LINKS = hr_HR
+
+DELINKS = de_AT de_CH
+
+.SUFFIXES: .src .out
+
+.src.out:
+ ${COLLDEF} -I ${.CURDIR} -o ${.TARGET} ${.IMPSRC}
+
+all: ${LOCALES:S/$/.out/g}
+
+ru_SU.KOI8-R.out: map.KOI8-R
+ru_SU.CP866.out: map.CP866
+lt_LN.ISO_8859-1.out: map.ISO_8859-1
+lt_LN.ISO_8859-2.out: map.ISO_8859-2
+es_ES.ISO_8859-1.out: map.ISO_8859-1
+de_DE.ISO_8859-1.out: map.ISO_8859-1
+
+afterinstall:
+.for locale in ${LOCALES}
+ ${INSTALL} -c -m 644 -o ${BINOWN} -g ${BINGRP} \
+ ${locale}.out ${LOCALEDIR}/${locale}/LC_COLLATE
+.endfor
+.for link in ${ASCIILINKS}
+ ln -fs ../lt_LN.ASCII/LC_COLLATE \
+ ${LOCALEDIR}/${link}/LC_COLLATE
+.endfor
+.for link in ${LATIN1LINKS}
+ ln -fs ../lt_LN.ISO_8859-1/LC_COLLATE \
+ ${LOCALEDIR}/${link}.ISO_8859-1/LC_COLLATE
+.endfor
+.for link in ${LATIN2LINKS}
+ ln -fs ../lt_LN.ISO_8859-2/LC_COLLATE \
+ ${LOCALEDIR}/${link}.ISO_8859-2/LC_COLLATE
+.endfor
+.for link in ${DELINKS}
+ ln -fs ../de_DE.ISO_8859-1/LC_COLLATE \
+ ${LOCALEDIR}/${link}.ISO_8859-1/LC_COLLATE
+.endfor
+
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/colldef/data/de_DE.ISO_8859-1.src b/usr.bin/colldef/data/de_DE.ISO_8859-1.src
new file mode 100644
index 0000000..c7afa46
--- /dev/null
+++ b/usr.bin/colldef/data/de_DE.ISO_8859-1.src
@@ -0,0 +1,38 @@
+# German/ISO 8859-1 (backward compatible with ASCII)
+#
+# $Id: de_DE.ISO_8859-1.src,v 1.3 1997/02/22 19:54:34 peter Exp $
+#
+charmap map.ISO_8859-1
+order \
+# controls
+ <NU>;...;<US>;<PA>;...;<AC>;\
+#
+ <NS>;<SP>;!;<!I>;\";<<<>;</>/>>;<Nb>;\
+ <Cu>;<Ct>;<DO>;<Pd>;<Ye>;\
+ %;&;<',>;';\(;\);*;+;<+->;<-:>;<*X>;\,;<-->;-;.;/;\
+# digits
+ (0,<14>,<12>,<34>);(1,<1S>);(2,<2S>);(3,<3S>);4;...;9;\
+#
+ :;\;;\<;=;>;?;<?I>;<SE>;<PI>;<Co>;<Rg>;<At>;\
+# capital
+ (A,<A'>,<A!>,<A/>>,<AA>,<A:>,<A?>,<AE>);\
+ B;(C,<C,>);D;(E,<E'>,<E!>,<E/>>,<E:>);\
+ F;G;H;(I,<I'>,<I!>,<I/>>,<I:>);\
+ J;...;M;(N,<N?>);(O,<O'>,<O!>,<O/>>,<O:>,<O?>,<O//>);\
+ P;...;T;(U,<U'>,<U!>,<U/>>,<U:>);\
+ V;W;X;(Y,<Y'>);Z;\
+ <D->;<TH>;\
+#
+ [;\\;];^;<':>;_;<'m>;<''>;`;\
+# small
+ (a,<a'>,<a!>,<a/>>,<aa>,<a:>,<a?>,<ae>);\
+ b;(c,<c,>);d;(e,<e'>,<e!>,<e/>>,<e:>);\
+ f;g;h;(i,<i'>,<i!>,<i/>>,<i:>);\
+ j;...;m;(n,<n?>);(o,<o'>,<o!>,<o/>>,<o:>,<o?>,<o//>);\
+ p;...;r;s;(<ss>,ss);t;(u,<u'>,<u!>,<u/>>,<u:>);\
+ v;w;x;(y,<y'>,<y:>);z;\
+ <d->;<th>;\
+#
+ \{;<NO>;|;<BB>;\};~;<.M>;<DG>;<My>;<DT>;\
+# remains
+ <-a>;<-o>
diff --git a/usr.bin/colldef/data/es_ES.ISO_8859-1.src b/usr.bin/colldef/data/es_ES.ISO_8859-1.src
new file mode 100644
index 0000000..e088e21
--- /dev/null
+++ b/usr.bin/colldef/data/es_ES.ISO_8859-1.src
@@ -0,0 +1,38 @@
+# Espan~ol (backward compatible with ASCII)
+#
+# $Id$
+#
+charmap map.ISO_8859-1
+order \
+# controls
+ <NU>;...;<US>;<PA>;...;<AC>;\
+#
+ <NS>;<SP>;!;<!I>;\";<<<>;</>/>>;<Nb>;\
+ <Cu>;<Ct>;<DO>;<Pd>;<Ye>;\
+ %;&;<',>;';\(;\);*;+;<+->;<-:>;<*X>;\,;<-->;-;.;/;\
+# digits
+ (0,<14>,<12>,<34>);(1,<1S>);(2,<2S>);(3,<3S>);4;...;9;\
+#
+ :;\;;\<;=;>;?;<?I>;<SE>;<PI>;<Co>;<Rg>;<At>;\
+# capital
+ (A,<A'>,<A!>,<A/>>,<AA>,<A:>,<A?>,<AE>);\
+ B;(C,<C,>);{CH,Ch};D;(E,<E'>,<E!>,<E/>>,<E:>);\
+ F;G;H;(I,<I'>,<I!>,<I/>>,<I:>);\
+ J;...;K;L;{LL,Ll};M;(N,<N?>);(O,<O'>,<O!>,<O/>>,<O:>,<O?>,<O//>);\
+ P;Q;R;{RR,Rr};S;T;(U,<U'>,<U!>,<U/>>,<U:>);\
+ V;W;X;(Y,<Y'>);Z;\
+ <D->;<TH>;\
+#
+ [;\\;];^;<':>;_;<'m>;<''>;`;\
+# small
+ (a,<a'>,<a!>,<a/>>,<aa>,<a:>,<a?>,<ae>);\
+ b;(c,<c,>);ch;d;(e,<e'>,<e!>,<e/>>,<e:>);\
+ f;g;h;(i,<i'>,<i!>,<i/>>,<i:>);\
+ j;...;k;l;ll;m;(n,<n?>);(o,<o'>,<o!>,<o/>>,<o:>,<o?>,<o//>);\
+ p;q;r;rr;s;t;(u,<u'>,<u!>,<u/>>,<u:>);\
+ v;w;x;(y,<y'>,<y:>);z;\
+ <d->;<th>;<ss>;\
+#
+ \{;<NO>;|;<BB>;\};~;<.M>;<DG>;<My>;<DT>;\
+# remains
+ <-a>;<-o>
diff --git a/usr.bin/colldef/data/is_IS.ISO_8859-1.src b/usr.bin/colldef/data/is_IS.ISO_8859-1.src
new file mode 100644
index 0000000..288459c
--- /dev/null
+++ b/usr.bin/colldef/data/is_IS.ISO_8859-1.src
@@ -0,0 +1,38 @@
+# icelandic (backward compatible with ASCII)
+#
+# $Id$
+#
+charmap map.ISO_8859-1
+order \
+# controls
+ <NU>;...;<US>;<PA>;...;<AC>;\
+#
+ <NS>;<SP>;!;<!I>;\";<<<>;</>/>>;<Nb>;\
+ <Cu>;<Ct>;<DO>;<Pd>;<Ye>;\
+ %;&;<',>;';\(;\);*;+;<+->;<-:>;<*X>;\,;<-->;-;.;/;\
+# digits
+ (0,<14>,<12>,<34>);(1,<1S>);(2,<2S>);(3,<3S>);4;...;9;\
+#
+ :;\;;\<;=;>;?;<?I>;<SE>;<PI>;<Co>;<Rg>;<At>;\
+# capital
+ (A,<A'>,<A!>,<A/>>,<AA>,<A:>,<A?>);\
+ B;(C,<C,>);(D,<D->);(E,<E'>,<E!>,<E/>>,<E:>);\
+ F;G;H;(I,<I'>,<I!>,<I/>>,<I:>);\
+ J;...;M;(N,<N?>);(O,<O'>,<O!>,<O/>>,<O?>,<O//>);\
+ P;...;T;(U,<U'>,<U!>,<U/>>,<U:>);\
+ V;W;X;(Y,<Y'>);Z;\
+ <TH>;<AE>;<O:>;\
+#
+ [;\\;];^;<':>;_;<'m>;<''>;`;\
+# small
+ (a,<a'>,<a!>,<a/>>,<aa>,<a:>,<a?>);\
+ b;(c,<c,>);(d,<d->);(e,<e'>,<e!>,<e/>>,<e:>);\
+ f;g;h;(i,<i'>,<i!>,<i/>>,<i:>);\
+ j;...;m;(n,<n?>);(o,<o'>,<o!>,<o/>>,<o?>,<o//>);\
+ p;...;t;(u,<u'>,<u!>,<u/>>,<u:>);\
+ v;w;x;(y,<y'>,<y:>);z;\
+ <th>;<ae>;<o:>;<ss>;\
+#
+ \{;<NO>;|;<BB>;\};~;<.M>;<DG>;<My>;<DT>;\
+# remains
+ <-a>;<-o>
diff --git a/usr.bin/colldef/data/lt_LN.ASCII.src b/usr.bin/colldef/data/lt_LN.ASCII.src
new file mode 100644
index 0000000..525ebb9
--- /dev/null
+++ b/usr.bin/colldef/data/lt_LN.ASCII.src
@@ -0,0 +1,6 @@
+# ASCII
+#
+# $Id: lt_LN.ISO_8859-1.src,v 1.8 1997/02/22 19:54:39 peter Exp $
+#
+order \
+ \x00;...;\xff
diff --git a/usr.bin/colldef/data/lt_LN.ISO_8859-1.src b/usr.bin/colldef/data/lt_LN.ISO_8859-1.src
new file mode 100644
index 0000000..93a2da0
--- /dev/null
+++ b/usr.bin/colldef/data/lt_LN.ISO_8859-1.src
@@ -0,0 +1,38 @@
+# latin1 (backward compatible with ASCII)
+#
+# $Id$
+#
+charmap map.ISO_8859-1
+order \
+# controls
+ <NU>;...;<US>;<PA>;...;<AC>;\
+#
+ <NS>;<SP>;!;<!I>;\";<<<>;</>/>>;<Nb>;\
+ <Cu>;<Ct>;<DO>;<Pd>;<Ye>;\
+ %;&;<',>;';\(;\);*;+;<+->;<-:>;<*X>;\,;<-->;-;.;/;\
+# digits
+ (0,<14>,<12>,<34>);(1,<1S>);(2,<2S>);(3,<3S>);4;...;9;\
+#
+ :;\;;\<;=;>;?;<?I>;<SE>;<PI>;<Co>;<Rg>;<At>;\
+# capital
+ (A,<A'>,<A!>,<A/>>,<AA>,<A:>,<A?>,<AE>);\
+ B;(C,<C,>);D;(E,<E'>,<E!>,<E/>>,<E:>);\
+ F;G;H;(I,<I'>,<I!>,<I/>>,<I:>);\
+ J;...;M;(N,<N?>);(O,<O'>,<O!>,<O/>>,<O:>,<O?>,<O//>);\
+ P;...;T;(U,<U'>,<U!>,<U/>>,<U:>);\
+ V;W;X;(Y,<Y'>);Z;\
+ <D->;<TH>;\
+#
+ [;\\;];^;<':>;_;<'m>;<''>;`;\
+# small
+ (a,<a'>,<a!>,<a/>>,<aa>,<a:>,<a?>,<ae>);\
+ b;(c,<c,>);d;(e,<e'>,<e!>,<e/>>,<e:>);\
+ f;g;h;(i,<i'>,<i!>,<i/>>,<i:>);\
+ j;...;m;(n,<n?>);(o,<o'>,<o!>,<o/>>,<o:>,<o?>,<o//>);\
+ p;...;t;(u,<u'>,<u!>,<u/>>,<u:>);\
+ v;w;x;(y,<y'>,<y:>);z;\
+ <d->;<th>;<ss>;\
+#
+ \{;<NO>;|;<BB>;\};~;<.M>;<DG>;<My>;<DT>;\
+# remains
+ <-a>;<-o>
diff --git a/usr.bin/colldef/data/lt_LN.ISO_8859-2.src b/usr.bin/colldef/data/lt_LN.ISO_8859-2.src
new file mode 100644
index 0000000..772fe32
--- /dev/null
+++ b/usr.bin/colldef/data/lt_LN.ISO_8859-2.src
@@ -0,0 +1,36 @@
+# latin2 (backward compatible with ASCII)
+#
+# $Id$
+#
+charmap map.ISO_8859-2
+order \
+# controls
+ <NU>;...;<US>;<PA>;...;<AC>;\
+#
+ <NS>;<SP>;!;\";<Nb>;\
+ <Cu>;<DO>;\
+ %;&;<',>;<';>;';\(;\);*;+;<-:>;<*X>;\,;<-->;-;.;<'.>;/;\
+# digits
+ 0;...;9;\
+#
+ :;\;;\<;=;>;?;<SE>;<At>;\
+# capital
+ (A,<A'>,<A/>>,<A:>,<A;>,<A(>);\
+ B;(C,<C,>,<C'>,<C<>);(D,<D<>,<D//>);(E,<E'>,<E:>,<E;>,<E<>);\
+ F;G;H;(I,<I'>,<I/>>);\
+ J;K;(L,<L//>,<L<>,<L'>);M;(N,<N'>,<N<>);(O,<O'>,<O/>>,<O:>,<O">);\
+ P;Q;(R,<R'>,<R<>);(S,<S'>,<S<>,<S,>);(T,<T<>,<T,>);\
+ (U,<U'>,<U:>,<U0>,<U">);\
+ V;W;X;(Y,<Y'>);(Z,<Z'>,<Z<>,<Z.>);\
+#
+ [;\\;];^;<':>;<'">;<'<>;<'(>;_;<''>;`;\
+# small
+ (a,<a'>,<a/>>,<a:>,<a;>,<a(>);\
+ b;(c,<c,>,<c'>,<c<>);(d,<d<>,<d//>);(e,<e'>,<e:>,<e;>,<e<>);\
+ f;g;h;(i,<i'>,<i/>>);\
+ j;k;(l,<l//>,<l<>,<l'>);m;(n,<n'>,<n<>);(o,<o'>,<o/>>,<o:>,<o">);\
+ p;q;(r,<r'>,<r<>);(s,<s'>,<s<>,<s,>,<ss>);(t,<t<>,<t,>);\
+ (u,<u'>,<u:>,<u0>,<u">);\
+ v;w;x;(y,<y'>);(z,<z'>,<z<>,<z.>);\
+#
+ \{;|;\};~;<DG>;<DT>
diff --git a/usr.bin/colldef/data/map.CP866 b/usr.bin/colldef/data/map.CP866
new file mode 100644
index 0000000..0311771
--- /dev/null
+++ b/usr.bin/colldef/data/map.CP866
@@ -0,0 +1,174 @@
+NU \x00
+SH \x01
+SX \x02
+EX \x03
+ET \x04
+EQ \x05
+AK \x06
+BL \x07
+BS \x08
+HT \x09
+LF \x0a
+VT \x0b
+FF \x0c
+CR \x0d
+SO \x0e
+SI \x0f
+DL \x10
+D1 \x11
+D2 \x12
+D3 \x13
+D4 \x14
+NK \x15
+SY \x16
+EB \x17
+CN \x18
+EM \x19
+SB \x1a
+EC \x1b
+FS \x1c
+GS \x1d
+RS \x1e
+US \x1f
+SP \x20
+Nb \x23
+DO \x24
+At \x40
+<( \x5b
+// \x5c
+)> \x5d
+'> \x5e
+'! \x60
+(! \x7b
+!! \x7c
+!) \x7d
+'? \x7e
+DT \x7f
+hh \xc4
+vv \xb3
+dr \xda
+dl \xbf
+ur \xc0
+ul \xd9
+vr \xc3
+vl \xb4
+dh \xc2
+uh \xc1
+vh \xc5
+TB \xdf
+LB \xdc
+FB \xdb
+lB \xdd
+RB \xde
+.S \xb0
+:S \xb1
+?S \xb2
+Iu \xf4
+fS \xfe
+sb \xf9
+RT \xfb
+?2 \xf7
+=< \xf3
+>= \xf2
+NS \xff
+Il \xf5
+DG \xf8
+2S \xfd
+.M \xfa
+-: \xf6
+HH \xcd
+VV \xba
+dR \xd5
+io \xf1
+Dr \xd6
+DR \xc9
+dL \xb8
+Dl \xb7
+LD \xbb
+uR \xd4
+Ur \xd3
+UR \xc8
+uL \xbe
+Ul \xbd
+UL \xbc
+vR \xc6
+Vr \xc7
+VR \xcc
+vL \xb5
+IO \xf0
+Vl \xb6
+VL \xb9
+dH \xd1
+Dh \xd2
+DH \xcb
+uH \xcf
+Uh \xd0
+UH \xca
+vH \xd8
+Vh \xd7
+VH \xce
+Co \xfc
+ju \xee
+a= \xa0
+b= \xa1
+c= \xe6
+d= \xa4
+e= \xa5
+f= \xe4
+g= \xa3
+h= \xe5
+i= \xa8
+j= \xa9
+k= \xaa
+l= \xab
+m= \xac
+n= \xad
+o= \xae
+p= \xaf
+ja \xef
+r= \xe0
+s= \xe1
+t= \xe2
+u= \xe3
+z% \xa6
+v= \xa2
+%' \xec
+y= \xeb
+z= \xa7
+s% \xe8
+je \xed
+sc \xe9
+c% \xe7
+=' \xea
+JU \x9e
+A= \x80
+B= \x81
+C= \x96
+D= \x84
+E= \x85
+F= \x94
+G= \x83
+H= \x95
+I= \x88
+J= \x89
+K= \x8a
+L= \x8b
+M= \x8c
+N= \x8d
+O= \x8e
+P= \x8f
+JA \x9f
+R= \x90
+S= \x91
+T= \x92
+U= \x93
+Z% \x86
+V= \x82
+%" \x9c
+Y= \x9b
+Z= \x87
+S% \x98
+JE \x9d
+Sc \x99
+C% \x97
+=" \x9a
diff --git a/usr.bin/colldef/data/map.ISO_8859-1 b/usr.bin/colldef/data/map.ISO_8859-1
new file mode 100644
index 0000000..ee5a557
--- /dev/null
+++ b/usr.bin/colldef/data/map.ISO_8859-1
@@ -0,0 +1,174 @@
+NU \x00
+SH \x01
+SX \x02
+EX \x03
+ET \x04
+EQ \x05
+AK \x06
+BL \x07
+BS \x08
+HT \x09
+LF \x0a
+VT \x0b
+FF \x0c
+CR \x0d
+SO \x0e
+SI \x0f
+DL \x10
+D1 \x11
+D2 \x12
+D3 \x13
+D4 \x14
+NK \x15
+SY \x16
+EB \x17
+CN \x18
+EM \x19
+SB \x1a
+EC \x1b
+FS \x1c
+GS \x1d
+RS \x1e
+US \x1f
+SP \x20
+Nb \x23
+DO \x24
+At \x40
+<( \x5b
+// \x5c
+)> \x5d
+'> \x5e
+'! \x60
+(! \x7b
+!! \x7c
+!) \x7d
+'? \x7e
+DT \x7f
+PA \x80
+HO \x81
+BH \x82
+NH \x83
+IN \x84
+NL \x85
+SA \x86
+ES \x87
+HS \x88
+HJ \x89
+VS \x8a
+PD \x8b
+PU \x8c
+RI \x8d
+S2 \x8e
+S3 \x8f
+DC \x90
+P1 \x91
+P2 \x92
+TS \x93
+CC \x94
+MW \x95
+SG \x96
+EG \x97
+SS \x98
+GC \x99
+SC \x9a
+CI \x9b
+ST \x9c
+OC \x9d
+PM \x9e
+AC \x9f
+NS \xa0
+!I \xa1
+Ct \xa2
+Pd \xa3
+Cu \xa4
+Ye \xa5
+BB \xa6
+SE \xa7
+': \xa8
+Co \xa9
+-a \xaa
+<< \xab
+NO \xac
+-- \xad
+Rg \xae
+'m \xaf
+DG \xb0
++- \xb1
+2S \xb2
+3S \xb3
+'' \xb4
+My \xb5
+PI \xb6
+.M \xb7
+', \xb8
+1S \xb9
+-o \xba
+>> \xbb
+14 \xbc
+12 \xbd
+34 \xbe
+?I \xbf
+A! \xc0
+A' \xc1
+A> \xc2
+A? \xc3
+A: \xc4
+AA \xc5
+AE \xc6
+C, \xc7
+E! \xc8
+E' \xc9
+E> \xca
+E: \xcb
+I! \xcc
+I' \xcd
+I> \xce
+I: \xcf
+D- \xd0
+N? \xd1
+O! \xd2
+O' \xd3
+O> \xd4
+O? \xd5
+O: \xd6
+*X \xd7
+O/ \xd8
+U! \xd9
+U' \xda
+U> \xdb
+U: \xdc
+Y' \xdd
+TH \xde
+ss \xdf
+a! \xe0
+a' \xe1
+a> \xe2
+a? \xe3
+a: \xe4
+aa \xe5
+ae \xe6
+c, \xe7
+e! \xe8
+e' \xe9
+e> \xea
+e: \xeb
+i! \xec
+i' \xed
+i> \xee
+i: \xef
+d- \xf0
+n? \xf1
+o! \xf2
+o' \xf3
+o> \xf4
+o? \xf5
+o: \xf6
+-: \xf7
+o/ \xf8
+u! \xf9
+u' \xfa
+u> \xfb
+u: \xfc
+y' \xfd
+th \xfe
+y: \xff
diff --git a/usr.bin/colldef/data/map.ISO_8859-2 b/usr.bin/colldef/data/map.ISO_8859-2
new file mode 100644
index 0000000..75f2013
--- /dev/null
+++ b/usr.bin/colldef/data/map.ISO_8859-2
@@ -0,0 +1,174 @@
+NU \x00
+SH \x01
+SX \x02
+EX \x03
+ET \x04
+EQ \x05
+AK \x06
+BL \x07
+BS \x08
+HT \x09
+LF \x0a
+VT \x0b
+FF \x0c
+CR \x0d
+SO \x0e
+SI \x0f
+DL \x10
+D1 \x11
+D2 \x12
+D3 \x13
+D4 \x14
+NK \x15
+SY \x16
+EB \x17
+CN \x18
+EM \x19
+SB \x1a
+EC \x1b
+FS \x1c
+GS \x1d
+RS \x1e
+US \x1f
+SP \x20
+Nb \x23
+DO \x24
+At \x40
+<( \x5b
+// \x5c
+)> \x5d
+'> \x5e
+'! \x60
+(! \x7b
+!! \x7c
+!) \x7d
+'? \x7e
+DT \x7f
+PA \x80
+HO \x81
+BH \x82
+NH \x83
+IN \x84
+NL \x85
+SA \x86
+ES \x87
+HS \x88
+HJ \x89
+VS \x8a
+PD \x8b
+PU \x8c
+RI \x8d
+S2 \x8e
+S3 \x8f
+DC \x90
+P1 \x91
+P2 \x92
+TS \x93
+CC \x94
+MW \x95
+SG \x96
+EG \x97
+SS \x98
+GC \x99
+SC \x9a
+CI \x9b
+ST \x9c
+OC \x9d
+PM \x9e
+AC \x9f
+NS \xa0
+A; \xa1
+'( \xa2
+L/ \xa3
+Cu \xa4
+L< \xa5
+S' \xa6
+SE \xa7
+': \xa8
+S< \xa9
+S, \xaa
+T< \xab
+Z' \xac
+-- \xad
+Z< \xae
+Z. \xaf
+DG \xb0
+a; \xb1
+'; \xb2
+l/ \xb3
+'' \xb4
+l< \xb5
+s' \xb6
+'< \xb7
+', \xb8
+s< \xb9
+s, \xba
+t< \xbb
+z' \xbc
+'" \xbd
+z< \xbe
+z. \xbf
+R' \xc0
+A' \xc1
+A> \xc2
+A( \xc3
+A: \xc4
+L' \xc5
+C' \xc6
+C, \xc7
+C< \xc8
+E' \xc9
+E; \xca
+E: \xcb
+E< \xcc
+I' \xcd
+I> \xce
+D< \xcf
+D/ \xd0
+N' \xd1
+N< \xd2
+O' \xd3
+O> \xd4
+O" \xd5
+O: \xd6
+*X \xd7
+R< \xd8
+U0 \xd9
+U' \xda
+U" \xdb
+U: \xdc
+Y' \xdd
+T, \xde
+ss \xdf
+r' \xe0
+a' \xe1
+a> \xe2
+a( \xe3
+a: \xe4
+l' \xe5
+c' \xe6
+c, \xe7
+c< \xe8
+e' \xe9
+e; \xea
+e: \xeb
+e< \xec
+i' \xed
+i> \xee
+d< \xef
+d/ \xf0
+n' \xf1
+n< \xf2
+o' \xf3
+o> \xf4
+o" \xf5
+o: \xf6
+-: \xf7
+r< \xf8
+u0 \xf9
+u' \xfa
+u" \xfb
+u: \xfc
+y' \xfd
+t, \xfe
+'. \xff
diff --git a/usr.bin/colldef/data/map.KOI8-R b/usr.bin/colldef/data/map.KOI8-R
new file mode 100644
index 0000000..180568f
--- /dev/null
+++ b/usr.bin/colldef/data/map.KOI8-R
@@ -0,0 +1,174 @@
+NU \x00
+SH \x01
+SX \x02
+EX \x03
+ET \x04
+EQ \x05
+AK \x06
+BL \x07
+BS \x08
+HT \x09
+LF \x0a
+VT \x0b
+FF \x0c
+CR \x0d
+SO \x0e
+SI \x0f
+DL \x10
+D1 \x11
+D2 \x12
+D3 \x13
+D4 \x14
+NK \x15
+SY \x16
+EB \x17
+CN \x18
+EM \x19
+SB \x1a
+EC \x1b
+FS \x1c
+GS \x1d
+RS \x1e
+US \x1f
+SP \x20
+Nb \x23
+DO \x24
+At \x40
+<( \x5b
+// \x5c
+)> \x5d
+'> \x5e
+'! \x60
+(! \x7b
+!! \x7c
+!) \x7d
+'? \x7e
+DT \x7f
+hh \x80
+vv \x81
+dr \x82
+dl \x83
+ur \x84
+ul \x85
+vr \x86
+vl \x87
+dh \x88
+uh \x89
+vh \x8a
+TB \x8b
+LB \x8c
+FB \x8d
+lB \x8e
+RB \x8f
+.S \x90
+:S \x91
+?S \x92
+Iu \x93
+fS \x94
+sb \x95
+RT \x96
+?2 \x97
+=< \x98
+>= \x99
+NS \x9a
+Il \x9b
+DG \x9c
+2S \x9d
+.M \x9e
+-: \x9f
+HH \xa0
+VV \xa1
+dR \xa2
+io \xa3
+Dr \xa4
+DR \xa5
+dL \xa6
+Dl \xa7
+LD \xa8
+uR \xa9
+Ur \xaa
+UR \xab
+uL \xac
+Ul \xad
+UL \xae
+vR \xaf
+Vr \xb0
+VR \xb1
+vL \xb2
+IO \xb3
+Vl \xb4
+VL \xb5
+dH \xb6
+Dh \xb7
+DH \xb8
+uH \xb9
+Uh \xba
+UH \xbb
+vH \xbc
+Vh \xbd
+VH \xbe
+Co \xbf
+ju \xc0
+a= \xc1
+b= \xc2
+c= \xc3
+d= \xc4
+e= \xc5
+f= \xc6
+g= \xc7
+h= \xc8
+i= \xc9
+j= \xca
+k= \xcb
+l= \xcc
+m= \xcd
+n= \xce
+o= \xcf
+p= \xd0
+ja \xd1
+r= \xd2
+s= \xd3
+t= \xd4
+u= \xd5
+z% \xd6
+v= \xd7
+%' \xd8
+y= \xd9
+z= \xda
+s% \xdb
+je \xdc
+sc \xdd
+c% \xde
+=' \xdf
+JU \xe0
+A= \xe1
+B= \xe2
+C= \xe3
+D= \xe4
+E= \xe5
+F= \xe6
+G= \xe7
+H= \xe8
+I= \xe9
+J= \xea
+K= \xeb
+L= \xec
+M= \xed
+N= \xee
+O= \xef
+P= \xf0
+JA \xf1
+R= \xf2
+S= \xf3
+T= \xf4
+U= \xf5
+Z% \xf6
+V= \xf7
+%" \xf8
+Y= \xf9
+Z= \xfa
+S% \xfb
+JE \xfc
+Sc \xfd
+C% \xfe
+=" \xff
diff --git a/usr.bin/colldef/data/ru_SU.CP866.src b/usr.bin/colldef/data/ru_SU.CP866.src
new file mode 100644
index 0000000..88ca377
--- /dev/null
+++ b/usr.bin/colldef/data/ru_SU.CP866.src
@@ -0,0 +1,39 @@
+# CP866 (backward compatible with ASCII)
+#
+# $Id$
+#
+charmap map.CP866
+order \
+# controls
+ <NU>;...;<US>;\
+#
+ <NS>;<SP>;!;\";<Nb>;<DO>;\
+ %;&;';\(;\);*;+;<-:>;\,;-;.;/;\
+# digits
+ 0;1;(2,<2S>);3;...;9;\
+#
+ :;\;;\<;<=<>;=;</>=>;>;?;<Co>;<At>;\
+# capital
+ A;...;Z;\
+ <A=>;<B=>;<V=>;<G=>;<D=>;<E=>;<IO>;<Z%>;<Z=>;\
+ <I=>;<J=>;<K=>;<L=>;<M=>;<N=>;<O=>;<P=>;<R=>;\
+ <S=>;<T=>;<U=>;<F=>;<H=>;<C=>;<C%>;<S%>;<Sc>;\
+ <=">;<Y=>;<%">;<JE>;<JU>;<JA>;\
+#
+ [;\\;];^;_;`;\
+# small
+ a;...;z;\
+ <a=>;<b=>;<v=>;<g=>;<d=>;<e=>;<io>;<z%>;<z=>;\
+ <i=>;<j=>;<k=>;<l=>;<m=>;<n=>;<o=>;<p=>;<r=>;\
+ <s=>;<t=>;<u=>;<f=>;<h=>;<c=>;<c%>;<s%>;<sc>;\
+ <='>;<y=>;<%'>;<je>;<ju>;<ja>;\
+#
+ \{;|;\};~;<.M>;<DG>;<DT>;\
+#
+ <sb>;<RT>;<?2>;<Iu>;<Il>;\
+ <hh>;<HH>;<vv>;<VV>;<dr>;<dR>;<Dr>;<DR>;\
+ <dl>;<dL>;<Dl>;<LD>;<ur>;<uR>;<Ur>;<UR>;\
+ <ul>;<uL>;<Ul>;<UL>;<vr>;<vR>;<Vr>;<VR>;\
+ <vl>;<vL>;<Vl>;<VL>;<dh>;<dH>;<Dh>;<DH>;\
+ <uh>;<uH>;<Uh>;<UH>;<vh>;<vH>;<Vh>;<VH>;\
+ <TB>;<LB>;<FB>;<lB>;<RB>;<.S>;<:S>;<?S>;<fS>
diff --git a/usr.bin/colldef/data/ru_SU.KOI8-R.src b/usr.bin/colldef/data/ru_SU.KOI8-R.src
new file mode 100644
index 0000000..f6b0a18
--- /dev/null
+++ b/usr.bin/colldef/data/ru_SU.KOI8-R.src
@@ -0,0 +1,39 @@
+# koi8-r (backward compatible with ASCII)
+#
+# $Id$
+#
+charmap map.KOI8-R
+order \
+# controls
+ <NU>;...;<US>;\
+#
+ <NS>;<SP>;!;\";<Nb>;<DO>;\
+ %;&;';\(;\);*;+;<-:>;\,;-;.;/;\
+# digits
+ 0;1;(2,<2S>);3;...;9;\
+#
+ :;\;;\<;<=<>;=;</>=>;>;?;<Co>;<At>;\
+# capital
+ A;...;Z;\
+ <A=>;<B=>;<V=>;<G=>;<D=>;<E=>;<IO>;<Z%>;<Z=>;\
+ <I=>;<J=>;<K=>;<L=>;<M=>;<N=>;<O=>;<P=>;<R=>;\
+ <S=>;<T=>;<U=>;<F=>;<H=>;<C=>;<C%>;<S%>;<Sc>;\
+ <=">;<Y=>;<%">;<JE>;<JU>;<JA>;\
+#
+ [;\\;];^;_;`;\
+# small
+ a;...;z;\
+ <a=>;<b=>;<v=>;<g=>;<d=>;<e=>;<io>;<z%>;<z=>;\
+ <i=>;<j=>;<k=>;<l=>;<m=>;<n=>;<o=>;<p=>;<r=>;\
+ <s=>;<t=>;<u=>;<f=>;<h=>;<c=>;<c%>;<s%>;<sc>;\
+ <='>;<y=>;<%'>;<je>;<ju>;<ja>;\
+#
+ \{;|;\};~;<.M>;<DG>;<DT>;\
+#
+ <sb>;<RT>;<?2>;<Iu>;<Il>;\
+ <hh>;<HH>;<vv>;<VV>;<dr>;<dR>;<Dr>;<DR>;\
+ <dl>;<dL>;<Dl>;<LD>;<ur>;<uR>;<Ur>;<UR>;\
+ <ul>;<uL>;<Ul>;<UL>;<vr>;<vR>;<Vr>;<VR>;\
+ <vl>;<vL>;<Vl>;<VL>;<dh>;<dH>;<Dh>;<DH>;\
+ <uh>;<uH>;<Uh>;<UH>;<vh>;<vH>;<Vh>;<VH>;\
+ <TB>;<LB>;<FB>;<lB>;<RB>;<.S>;<:S>;<?S>;<fS>
diff --git a/usr.bin/colldef/en_DK.example b/usr.bin/colldef/en_DK.example
new file mode 100644
index 0000000..c6777d2
--- /dev/null
+++ b/usr.bin/colldef/en_DK.example
@@ -0,0 +1,3073 @@
+escape_char /
+comment_char %
+
+% English language locale for Denmark
+%
+% Written according to POSIX.2
+% ISO/IEC 9945-2:1993 specifications
+%
+% Source: Danish Standards Association
+% Address: Baunegaardsvej 73,
+% DK-2900 Hellerup, Danmark
+% Contact: Keld Simonsen
+% Email: Keld.Simonsen@dkuug.dk
+% Tel: +45 - 39770101
+% Fax: +45 - 39770202
+% Language: en
+% Territory: DK
+% Revision: 3.3
+% Date: 1994-03-22
+% Application: general
+% Users: general
+% Repertoiremap: mnemonic.ds
+% Charset: ISO_8859-1:1987
+% Distribution and use is free, also for
+% commercial purposes.
+
+LC_COLLATE
+
+% Base collation scheme: 1994-03-22
+
+% Ordering algorithm:
+% 1. Spaces and hyphen (but not soft
+% hyphen) before punctuation
+% characters, punctuation characters
+% before numbers,
+% numbers before letters.
+% 2. Letters with diacritical marks are
+% members of equivalence classes
+% 3. A distinction is made with regards
+% to case as noted below.
+% 4. Special characters are ignored
+% when comparing letters, but then
+% they are considered
+% 5. The alphabets are sorted in order
+% of first appearance in ISO 10646:
+% Latin, Greek, Cyrillic, etc.
+%
+% According to Greek specifications,
+% the steps 2. and 3. above are reversed
+% for the Greek script
+
+% collating symbols
+
+% The collating symbol naming is
+% mostly taken from ISO 10646-1,
+% for example the case and accent
+% names are from this standard.
+
+collating-symbol <CAPITAL>
+collating-symbol <CAPITAL-SMALL>
+collating-symbol <SMALL-CAPITAL>
+collating-symbol <SMALL>
+
+% <CAPITAL-SMALL> and <SMALL-CAPITAL>
+% are for cases like Danish <A><a>
+% and Spanish <C><h> being treated
+% as one letter.
+
+% The <a8> ...... <z8> collating
+% symbols have defined weights as
+% the last character in a group of
+% Latin letters. They are used
+% to specify deltas by locales using
+% a locale as the default ordering
+% and by "replace-after" statements
+% specifying the changed placement
+% in an ordering of a character.
+
+collating-symbol <a8>
+collating-symbol <b8>
+collating-symbol <c8>
+collating-symbol <d8>
+collating-symbol <e8>
+collating-symbol <f8>
+collating-symbol <g8>
+collating-symbol <h8>
+collating-symbol <i8>
+collating-symbol <j8>
+collating-symbol <k8>
+collating-symbol <l8>
+collating-symbol <m8>
+collating-symbol <n8>
+collating-symbol <o8>
+collating-symbol <p8>
+collating-symbol <q8>
+collating-symbol <r8>
+collating-symbol <s8>
+collating-symbol <t8>
+collating-symbol <u8>
+collating-symbol <v8>
+collating-symbol <w8>
+collating-symbol <x8>
+collating-symbol <y8>
+collating-symbol <z8>
+
+collating-symbol <NONE>
+collating-symbol <ACUTE>
+collating-symbol <ACUTE+DOT>
+collating-symbol <GRAVE>
+collating-symbol <DOUBLE-GRAVE>
+collating-symbol <BREVE>
+collating-symbol <BREVE+ACUTE>
+collating-symbol <BREVE+GRAVE>
+collating-symbol <BREVE+MACRON>
+collating-symbol <BREVE+HOOK>
+collating-symbol <BREVE+TILDE>
+collating-symbol <BREVE+DOT-BELOW>
+collating-symbol <BREVE-BELOW>
+collating-symbol <INVERTED-BREVE>
+collating-symbol <CIRCUMFLEX>
+collating-symbol <CIRCUMFLEX+ACUTE>
+collating-symbol <CIRCUMFLEX+GRAVE>
+collating-symbol <CIRCUMFLEX+HOOK>
+collating-symbol <CIRCUMFLEX+TILDE>
+collating-symbol <CIRCUMFLEX+DOT-BELOW>
+collating-symbol <CARON>
+collating-symbol <CARON+DIAERESIS>
+collating-symbol <CARON+DOT>
+collating-symbol <RING>
+collating-symbol <RING+ACUTE>
+collating-symbol <RING-BELOW>
+collating-symbol <DIAERESIS>
+collating-symbol <DIAERESIS+MACRON>
+collating-symbol <DIAERESIS+ACUTE>
+collating-symbol <DIAERESIS+GRAVE>
+collating-symbol <DIAERESIS+CARON>
+collating-symbol <DOUBLE-ACUTE>
+collating-symbol <HOOK>
+collating-symbol <TILDE>
+collating-symbol <TILDE+ACUTE>
+collating-symbol <TILDE+DIAERESIS>
+collating-symbol <TILDE-BELOW>
+collating-symbol <DOT>
+collating-symbol <DOT-BELOW>
+collating-symbol <DOT+DOT-BELOW>
+collating-symbol <STROKE>
+collating-symbol <STROKE+ACUTE>
+collating-symbol <CEDILLA>
+collating-symbol <CEDILLA+ACUTE>
+collating-symbol <CEDILLA+GRAVE>
+collating-symbol <CEDILLA+BREVE>
+collating-symbol <OGONEK>
+collating-symbol <OGONEK+MACRON>
+collating-symbol <MACRON>
+collating-symbol <MACRON+ACUTE>
+collating-symbol <MACRON+GRAVE>
+collating-symbol <MACRON+DIAERESIS>
+collating-symbol <MACRON+DIAERESIS-BELOW>
+collating-symbol <MACRON+DOT>
+collating-symbol <MACRON+DOT-BELOW>
+collating-symbol <MACRON+CIRCUMFLEX>
+collating-symbol <LINE-BELOW>
+collating-symbol <HORN>
+collating-symbol <HORN+ACUTE>
+collating-symbol <HORN+GRAVE>
+collating-symbol <HORN+HOOK>
+collating-symbol <HORN+TILDE>
+collating-symbol <HORN+DOT-BELOW>
+collating-symbol <PRECEDED-BY-APOSTROPHE>
+collating-symbol <GREEK>
+collating-symbol <TONOS>
+collating-symbol <DIALYTICA>
+collating-symbol <DIALYTICA+TONOS>
+collating-symbol <CYRILLIC>
+collating-symbol <HIRAGANA>
+collating-symbol <KATAKANA>
+collating-symbol <SPECIAL>
+
+% letter;accent;case;specials
+
+order_start forward;backward/
+ ;forward;position
+
+% <CAPITAL> or <SMALL> letters first:
+
+<CAPITAL>
+<CAPITAL-SMALL>
+<SMALL-CAPITAL>
+<SMALL>
+
+% Accents:
+
+<NONE>
+<ACUTE>
+<ACUTE+DOT>
+<GRAVE>
+<DOUBLE-GRAVE>
+<BREVE>
+<BREVE+ACUTE>
+<BREVE+GRAVE>
+<BREVE+MACRON>
+<BREVE+HOOK>
+<BREVE+TILDE>
+<BREVE+DOT-BELOW>
+<BREVE-BELOW>
+<INVERTED-BREVE>
+<CIRCUMFLEX>
+<CIRCUMFLEX+ACUTE>
+<CIRCUMFLEX+GRAVE>
+<CIRCUMFLEX+HOOK>
+<CIRCUMFLEX+TILDE>
+<CIRCUMFLEX+DOT-BELOW>
+<CARON>
+<CARON+DIAERESIS>
+<CARON+DOT>
+<RING>
+<RING+ACUTE>
+<RING-BELOW>
+<DIAERESIS>
+<DIAERESIS+MACRON>
+<DIAERESIS+ACUTE>
+<DIAERESIS+GRAVE>
+<DIAERESIS+CARON>
+<DOUBLE-ACUTE>
+<HOOK>
+<TILDE>
+<TILDE+ACUTE>
+<TILDE+DIAERESIS>
+<TILDE-BELOW>
+<DOT>
+<DOT-BELOW>
+<DOT+DOT-BELOW>
+<STROKE>
+<STROKE+ACUTE>
+<CEDILLA>
+<CEDILLA+ACUTE>
+<CEDILLA+GRAVE>
+<CEDILLA+BREVE>
+<OGONEK>
+<OGONEK+MACRON>
+<MACRON>
+<MACRON+ACUTE>
+<MACRON+GRAVE>
+<MACRON+DIAERESIS>
+<MACRON+DIAERESIS-BELOW>
+<MACRON+DOT>
+<MACRON+DOT-BELOW>
+<MACRON+CIRCUMFLEX>
+<LINE-BELOW>
+<HORN>
+<HORN+ACUTE>
+<HORN+GRAVE>
+<HORN+HOOK>
+<HORN+TILDE>
+<HORN+DOT-BELOW>
+<PRECEDED-BY-APOSTROPHE>
+<GREEK>
+<TONOS>
+<DIALYTICA>
+<DIALYTICA+TONOS>
+<CYRILLIC>
+<HIRAGANA>
+<KATAKANA>
+<SPECIAL>
+
+<NS> <NS>;<NS>;<NS>;IGNORE
+<SP> IGNORE;IGNORE;IGNORE;<SP>
+<HT> IGNORE;IGNORE;IGNORE;<HT>
+<VT> IGNORE;IGNORE;IGNORE;<VT>
+<CR> IGNORE;IGNORE;IGNORE;<CR>
+<LF> IGNORE;IGNORE;IGNORE;<LF>
+<FF> IGNORE;IGNORE;IGNORE;<FF>
+<_> IGNORE;IGNORE;IGNORE;<_>
+<'m> IGNORE;IGNORE;IGNORE;<'m>
+<--> IGNORE;IGNORE;IGNORE;<-->
+<-> IGNORE;IGNORE;IGNORE;<->
+<,> IGNORE;IGNORE;IGNORE;<,>
+<;> IGNORE;IGNORE;IGNORE;<;>
+<:> IGNORE;IGNORE;IGNORE;<:>
+<!> IGNORE;IGNORE;IGNORE;<!>
+<!I> IGNORE;IGNORE;IGNORE;<!I>
+<?> IGNORE;IGNORE;IGNORE;<?>
+<?I> IGNORE;IGNORE;IGNORE;<?I>
+<//> IGNORE;IGNORE;IGNORE;<//>
+<.> IGNORE;IGNORE;IGNORE;<.>
+<''> IGNORE;IGNORE;IGNORE;<''>
+<'!> IGNORE;IGNORE;IGNORE;<'!>
+<'/>> IGNORE;IGNORE;IGNORE;<'/>>
+<':> IGNORE;IGNORE;IGNORE;<':>
+<'?> IGNORE;IGNORE;IGNORE;<'?>
+<.M> IGNORE;IGNORE;IGNORE;<.M>
+<',> IGNORE;IGNORE;IGNORE;<',>
+<'> IGNORE;IGNORE;IGNORE;<'>
+<'6> IGNORE;IGNORE;IGNORE;<'6>
+<'9> IGNORE;IGNORE;IGNORE;<'9>
+<"> IGNORE;IGNORE;IGNORE;<">
+<"6> IGNORE;IGNORE;IGNORE;<"6>
+<"9> IGNORE;IGNORE;IGNORE;<"9>
+<<<> IGNORE;IGNORE;IGNORE;<<<>
+</>/>> IGNORE;IGNORE;IGNORE;</>/>>
+<(> IGNORE;IGNORE;IGNORE;<(>
+<(S> IGNORE;IGNORE;IGNORE;<(S>
+<)> IGNORE;IGNORE;IGNORE;<)>
+<)S> IGNORE;IGNORE;IGNORE;<)S>
+<<(> IGNORE;IGNORE;IGNORE;<<(>
+<)/>> IGNORE;IGNORE;IGNORE;<)/>>
+<(!> IGNORE;IGNORE;IGNORE;<(!>
+<!)> IGNORE;IGNORE;IGNORE;<!)>
+<SE> IGNORE;IGNORE;IGNORE;<SE>
+<PI> IGNORE;IGNORE;IGNORE;<PI>
+<Co> IGNORE;IGNORE;IGNORE;<Co>
+<Rg> IGNORE;IGNORE;IGNORE;<Rg>
+<At> IGNORE;IGNORE;IGNORE;<At>
+<Cu> IGNORE;IGNORE;IGNORE;<Cu>
+<Ct> IGNORE;IGNORE;IGNORE;<Ct>
+<DO> IGNORE;IGNORE;IGNORE;<DO>
+<Pd> IGNORE;IGNORE;IGNORE;<Pd>
+<Ye> IGNORE;IGNORE;IGNORE;<Ye>
+<*> IGNORE;IGNORE;IGNORE;<*>
+<////> IGNORE;IGNORE;IGNORE;<////>
+<&> IGNORE;IGNORE;IGNORE;<&>
+<Nb> IGNORE;IGNORE;IGNORE;<Nb>
+<%> IGNORE;IGNORE;IGNORE;<%>
+<-S> IGNORE;IGNORE;IGNORE;<-S>
+<+> IGNORE;IGNORE;IGNORE;<+>
+<+S> IGNORE;IGNORE;IGNORE;<+S>
+<+-> IGNORE;IGNORE;IGNORE;<+->
+<-:> IGNORE;IGNORE;IGNORE;<-:>
+<*X> IGNORE;IGNORE;IGNORE;<*X>
+<!=> IGNORE;IGNORE;IGNORE;<!=>
+<<> IGNORE;IGNORE;IGNORE;<<>
+<=<> IGNORE;IGNORE;IGNORE;<=<>
+<=> IGNORE;IGNORE;IGNORE;<=>
+</>=> IGNORE;IGNORE;IGNORE;</>=>
+</>> IGNORE;IGNORE;IGNORE;</>>
+<NO> IGNORE;IGNORE;IGNORE;<NO>
+<!!> IGNORE;IGNORE;IGNORE;<!!>
+<BB> IGNORE;IGNORE;IGNORE;<BB>
+<DG> IGNORE;IGNORE;IGNORE;<DG>
+<My> IGNORE;IGNORE;IGNORE;<My>
+<'<> IGNORE;IGNORE;IGNORE;<'<>
+<'(> IGNORE;IGNORE;IGNORE;<'(>
+<'.> IGNORE;IGNORE;IGNORE;<'.>
+<'0> IGNORE;IGNORE;IGNORE;<'0>
+<';> IGNORE;IGNORE;IGNORE;<';>
+<1?> IGNORE;IGNORE;IGNORE;<1?>
+<'"> IGNORE;IGNORE;IGNORE;<'">
+<'G> IGNORE;IGNORE;IGNORE;<'G>
+<,G> IGNORE;IGNORE;IGNORE;<,G>
+<j3> IGNORE;IGNORE;IGNORE;<j3>
+<?%> IGNORE;IGNORE;IGNORE;<?%>
+<'*> IGNORE;IGNORE;IGNORE;<'*>
+<'%> IGNORE;IGNORE;IGNORE;<'%>
+<.*> IGNORE;IGNORE;IGNORE;<.*>
+<b3> IGNORE;IGNORE;IGNORE;<b3>
+<,,> IGNORE;IGNORE;IGNORE;<,,>
+<?*> IGNORE;IGNORE;IGNORE;<?*>
+<?:> IGNORE;IGNORE;IGNORE;<?:>
+<,!> IGNORE;IGNORE;IGNORE;<,!>
+<,'> IGNORE;IGNORE;IGNORE;<,'>
+<?,> IGNORE;IGNORE;IGNORE;<?,>
+<;!> IGNORE;IGNORE;IGNORE;<;!>
+<;'> IGNORE;IGNORE;IGNORE;<;'>
+<?;> IGNORE;IGNORE;IGNORE;<?;>
+<!:> IGNORE;IGNORE;IGNORE;<!:>
+<!*> IGNORE;IGNORE;IGNORE;<!*>
+<;;> IGNORE;IGNORE;IGNORE;<;;>
+<1N> IGNORE;IGNORE;IGNORE;<1N>
+<1M> IGNORE;IGNORE;IGNORE;<1M>
+<3M> IGNORE;IGNORE;IGNORE;<3M>
+<4M> IGNORE;IGNORE;IGNORE;<4M>
+<6M> IGNORE;IGNORE;IGNORE;<6M>
+<LR> IGNORE;IGNORE;IGNORE;<LR>
+<RL> IGNORE;IGNORE;IGNORE;<RL>
+<1T> IGNORE;IGNORE;IGNORE;<1T>
+<1H> IGNORE;IGNORE;IGNORE;<1H>
+<-1> IGNORE;IGNORE;IGNORE;<-1>
+<-N> IGNORE;IGNORE;IGNORE;<-N>
+<-M> IGNORE;IGNORE;IGNORE;<-M>
+<-3> IGNORE;IGNORE;IGNORE;<-3>
+<!2> IGNORE;IGNORE;IGNORE;<!2>
+<=2> IGNORE;IGNORE;IGNORE;<=2>
+<.9> IGNORE;IGNORE;IGNORE;<.9>
+<9'> IGNORE;IGNORE;IGNORE;<9'>
+<:9> IGNORE;IGNORE;IGNORE;<:9>
+<9"> IGNORE;IGNORE;IGNORE;<9">
+<//-> IGNORE;IGNORE;IGNORE;<//->
+<//=> IGNORE;IGNORE;IGNORE;<//=>
+<Sb> IGNORE;IGNORE;IGNORE;<Sb>
+<..> IGNORE;IGNORE;IGNORE;<..>
+<.3> IGNORE;IGNORE;IGNORE;<.3>
+<%0> IGNORE;IGNORE;IGNORE;<%0>
+<1'> IGNORE;IGNORE;IGNORE;<1'>
+<2'> IGNORE;IGNORE;IGNORE;<2'>
+<3'> IGNORE;IGNORE;IGNORE;<3'>
+<1"> IGNORE;IGNORE;IGNORE;<1">
+<2"> IGNORE;IGNORE;IGNORE;<2">
+<3"> IGNORE;IGNORE;IGNORE;<3">
+<Ca> IGNORE;IGNORE;IGNORE;<Ca>
+<<1> IGNORE;IGNORE;IGNORE;<<1>
+</>1> IGNORE;IGNORE;IGNORE;</>1>
+<:X> IGNORE;IGNORE;IGNORE;<:X>
+<!*2> IGNORE;IGNORE;IGNORE;<!*2>
+<'-> IGNORE;IGNORE;IGNORE;<'->
+<=S> IGNORE;IGNORE;IGNORE;<=S>
+<0s> IGNORE;IGNORE;IGNORE;<0s>
+<1s> IGNORE;IGNORE;IGNORE;<1s>
+<2s> IGNORE;IGNORE;IGNORE;<2s>
+<3s> IGNORE;IGNORE;IGNORE;<3s>
+<4s> IGNORE;IGNORE;IGNORE;<4s>
+<5s> IGNORE;IGNORE;IGNORE;<5s>
+<6s> IGNORE;IGNORE;IGNORE;<6s>
+<7s> IGNORE;IGNORE;IGNORE;<7s>
+<8s> IGNORE;IGNORE;IGNORE;<8s>
+<9s> IGNORE;IGNORE;IGNORE;<9s>
+<+s> IGNORE;IGNORE;IGNORE;<+s>
+<-s> IGNORE;IGNORE;IGNORE;<-s>
+<=s> IGNORE;IGNORE;IGNORE;<=s>
+<(s> IGNORE;IGNORE;IGNORE;<(s>
+<)s> IGNORE;IGNORE;IGNORE;<)s>
+<Ff> IGNORE;IGNORE;IGNORE;<Ff>
+<Li> IGNORE;IGNORE;IGNORE;<Li>
+<Pt> IGNORE;IGNORE;IGNORE;<Pt>
+<W=> IGNORE;IGNORE;IGNORE;<W=>
+<oC> IGNORE;IGNORE;IGNORE;<oC>
+<co> IGNORE;IGNORE;IGNORE;<co>
+<oF> IGNORE;IGNORE;IGNORE;<oF>
+<N0> IGNORE;IGNORE;IGNORE;<N0>
+<PO> IGNORE;IGNORE;IGNORE;<PO>
+<Rx> IGNORE;IGNORE;IGNORE;<Rx>
+<SM> IGNORE;IGNORE;IGNORE;<SM>
+<TM> IGNORE;IGNORE;IGNORE;<TM>
+<Om> IGNORE;IGNORE;IGNORE;<Om>
+<AO> IGNORE;IGNORE;IGNORE;<AO>
+<13> IGNORE;IGNORE;IGNORE;<13>
+<23> IGNORE;IGNORE;IGNORE;<23>
+<15> IGNORE;IGNORE;IGNORE;<15>
+<25> IGNORE;IGNORE;IGNORE;<25>
+<35> IGNORE;IGNORE;IGNORE;<35>
+<45> IGNORE;IGNORE;IGNORE;<45>
+<16> IGNORE;IGNORE;IGNORE;<16>
+<56> IGNORE;IGNORE;IGNORE;<56>
+<1R> IGNORE;IGNORE;IGNORE;<1R>
+<2R> IGNORE;IGNORE;IGNORE;<2R>
+<3R> IGNORE;IGNORE;IGNORE;<3R>
+<4R> IGNORE;IGNORE;IGNORE;<4R>
+<5R> IGNORE;IGNORE;IGNORE;<5R>
+<6R> IGNORE;IGNORE;IGNORE;<6R>
+<7R> IGNORE;IGNORE;IGNORE;<7R>
+<8R> IGNORE;IGNORE;IGNORE;<8R>
+<9R> IGNORE;IGNORE;IGNORE;<9R>
+<aR> IGNORE;IGNORE;IGNORE;<aR>
+<bR> IGNORE;IGNORE;IGNORE;<bR>
+<cR> IGNORE;IGNORE;IGNORE;<cR>
+<50R> IGNORE;IGNORE;IGNORE;<50R>
+<100R> IGNORE;IGNORE;IGNORE;<100R>
+<500R> IGNORE;IGNORE;IGNORE;<500R>
+<1000R> IGNORE;IGNORE;IGNORE;<1000R>
+<1r> IGNORE;IGNORE;IGNORE;<1r>
+<2r> IGNORE;IGNORE;IGNORE;<2r>
+<3r> IGNORE;IGNORE;IGNORE;<3r>
+<4r> IGNORE;IGNORE;IGNORE;<4r>
+<5r> IGNORE;IGNORE;IGNORE;<5r>
+<6r> IGNORE;IGNORE;IGNORE;<6r>
+<7r> IGNORE;IGNORE;IGNORE;<7r>
+<8r> IGNORE;IGNORE;IGNORE;<8r>
+<9r> IGNORE;IGNORE;IGNORE;<9r>
+<ar> IGNORE;IGNORE;IGNORE;<ar>
+<br> IGNORE;IGNORE;IGNORE;<br>
+<cr> IGNORE;IGNORE;IGNORE;<cr>
+<50r> IGNORE;IGNORE;IGNORE;<50r>
+<100r> IGNORE;IGNORE;IGNORE;<100r>
+<500r> IGNORE;IGNORE;IGNORE;<500r>
+<1000r> IGNORE;IGNORE;IGNORE;<1000r>
+<1000RCD> IGNORE;IGNORE;IGNORE;<1000RCD>
+<5000R> IGNORE;IGNORE;IGNORE;<5000R>
+<10000R> IGNORE;IGNORE;IGNORE;<10000R>
+<-!> IGNORE;IGNORE;IGNORE;<-!>
+<-v> IGNORE;IGNORE;IGNORE;<-v>
+<</>> IGNORE;IGNORE;IGNORE;<</>>
+<UD> IGNORE;IGNORE;IGNORE;<UD>
+<<!!> IGNORE;IGNORE;IGNORE;<<!!>
+</////>> IGNORE;IGNORE;IGNORE;</////>>
+<!!/>> IGNORE;IGNORE;IGNORE;<!!/>>
+<<////> IGNORE;IGNORE;IGNORE;<<////>
+<UD-> IGNORE;IGNORE;IGNORE;<UD->
+</>V> IGNORE;IGNORE;IGNORE;</>V>
+<<=> IGNORE;IGNORE;IGNORE;<<=>
+<=/>> IGNORE;IGNORE;IGNORE;<=/>>
+<==> IGNORE;IGNORE;IGNORE;<==>
+<FA> IGNORE;IGNORE;IGNORE;<FA>
+<dP> IGNORE;IGNORE;IGNORE;<dP>
+<TE> IGNORE;IGNORE;IGNORE;<TE>
+<//0> IGNORE;IGNORE;IGNORE;<//0>
+<DE> IGNORE;IGNORE;IGNORE;<DE>
+<NB> IGNORE;IGNORE;IGNORE;<NB>
+<(-> IGNORE;IGNORE;IGNORE;<(->
+<-)> IGNORE;IGNORE;IGNORE;<-)>
+<*P> IGNORE;IGNORE;IGNORE;<*P>
+<+Z> IGNORE;IGNORE;IGNORE;<+Z>
+<-2> IGNORE;IGNORE;IGNORE;<-2>
+<-+> IGNORE;IGNORE;IGNORE;<-+>
+<.+> IGNORE;IGNORE;IGNORE;<.+>
+<//f> IGNORE;IGNORE;IGNORE;<//f>
+<*-> IGNORE;IGNORE;IGNORE;<*->
+<Ob> IGNORE;IGNORE;IGNORE;<Ob>
+<sb> IGNORE;IGNORE;IGNORE;<sb>
+<RT> IGNORE;IGNORE;IGNORE;<RT>
+<0(> IGNORE;IGNORE;IGNORE;<0(>
+<00> IGNORE;IGNORE;IGNORE;<00>
+<-L> IGNORE;IGNORE;IGNORE;<-L>
+<-V> IGNORE;IGNORE;IGNORE;<-V>
+<PP> IGNORE;IGNORE;IGNORE;<PP>
+<AN> IGNORE;IGNORE;IGNORE;<AN>
+<OR> IGNORE;IGNORE;IGNORE;<OR>
+<(U> IGNORE;IGNORE;IGNORE;<(U>
+<)U> IGNORE;IGNORE;IGNORE;<)U>
+<In> IGNORE;IGNORE;IGNORE;<In>
+<DI> IGNORE;IGNORE;IGNORE;<DI>
+<Io> IGNORE;IGNORE;IGNORE;<Io>
+<.:> IGNORE;IGNORE;IGNORE;<.:>
+<:.> IGNORE;IGNORE;IGNORE;<:.>
+<:R> IGNORE;IGNORE;IGNORE;<:R>
+<::> IGNORE;IGNORE;IGNORE;<::>
+<?1> IGNORE;IGNORE;IGNORE;<?1>
+<CG> IGNORE;IGNORE;IGNORE;<CG>
+<?-> IGNORE;IGNORE;IGNORE;<?->
+<?=> IGNORE;IGNORE;IGNORE;<?=>
+<?2> IGNORE;IGNORE;IGNORE;<?2>
+<=?> IGNORE;IGNORE;IGNORE;<=?>
+<HI> IGNORE;IGNORE;IGNORE;<HI>
+<=3> IGNORE;IGNORE;IGNORE;<=3>
+<<*> IGNORE;IGNORE;IGNORE;<<*>
+<*/>> IGNORE;IGNORE;IGNORE;<*/>>
+<!<> IGNORE;IGNORE;IGNORE;<!<>
+<!/>> IGNORE;IGNORE;IGNORE;<!/>>
+<(C> IGNORE;IGNORE;IGNORE;<(C>
+<)C> IGNORE;IGNORE;IGNORE;<)C>
+<(_> IGNORE;IGNORE;IGNORE;<(_>
+<)_> IGNORE;IGNORE;IGNORE;<)_>
+<0.> IGNORE;IGNORE;IGNORE;<0.>
+<02> IGNORE;IGNORE;IGNORE;<02>
+<-T> IGNORE;IGNORE;IGNORE;<-T>
+<.P> IGNORE;IGNORE;IGNORE;<.P>
+<:3> IGNORE;IGNORE;IGNORE;<:3>
+<Eh> IGNORE;IGNORE;IGNORE;<Eh>
+<<7> IGNORE;IGNORE;IGNORE;<<7>
+</>7> IGNORE;IGNORE;IGNORE;</>7>
+<7<> IGNORE;IGNORE;IGNORE;<7<>
+<7/>> IGNORE;IGNORE;IGNORE;<7/>>
+<NI> IGNORE;IGNORE;IGNORE;<NI>
+<(A> IGNORE;IGNORE;IGNORE;<(A>
+<TR> IGNORE;IGNORE;IGNORE;<TR>
+<Iu> IGNORE;IGNORE;IGNORE;<Iu>
+<Il> IGNORE;IGNORE;IGNORE;<Il>
+<Vs> IGNORE;IGNORE;IGNORE;<Vs>
+<1h> IGNORE;IGNORE;IGNORE;<1h>
+<3h> IGNORE;IGNORE;IGNORE;<3h>
+<2h> IGNORE;IGNORE;IGNORE;<2h>
+<4h> IGNORE;IGNORE;IGNORE;<4h>
+<1j> IGNORE;IGNORE;IGNORE;<1j>
+<2j> IGNORE;IGNORE;IGNORE;<2j>
+<3j> IGNORE;IGNORE;IGNORE;<3j>
+<4j> IGNORE;IGNORE;IGNORE;<4j>
+<1-o> IGNORE;IGNORE;IGNORE;<1-o>
+<2-o> IGNORE;IGNORE;IGNORE;<2-o>
+<3-o> IGNORE;IGNORE;IGNORE;<3-o>
+<4-o> IGNORE;IGNORE;IGNORE;<4-o>
+<5-o> IGNORE;IGNORE;IGNORE;<5-o>
+<6-o> IGNORE;IGNORE;IGNORE;<6-o>
+<7-o> IGNORE;IGNORE;IGNORE;<7-o>
+<8-o> IGNORE;IGNORE;IGNORE;<8-o>
+<9-o> IGNORE;IGNORE;IGNORE;<9-o>
+<10-o> IGNORE;IGNORE;IGNORE;<10-o>
+<11-o> IGNORE;IGNORE;IGNORE;<11-o>
+<12-o> IGNORE;IGNORE;IGNORE;<12-o>
+<13-o> IGNORE;IGNORE;IGNORE;<13-o>
+<14-o> IGNORE;IGNORE;IGNORE;<14-o>
+<15-o> IGNORE;IGNORE;IGNORE;<15-o>
+<16-o> IGNORE;IGNORE;IGNORE;<16-o>
+<17-o> IGNORE;IGNORE;IGNORE;<17-o>
+<18-o> IGNORE;IGNORE;IGNORE;<18-o>
+<19-o> IGNORE;IGNORE;IGNORE;<19-o>
+<20-o> IGNORE;IGNORE;IGNORE;<20-o>
+<(1)> IGNORE;IGNORE;IGNORE;<(1)>
+<(2)> IGNORE;IGNORE;IGNORE;<(2)>
+<(3)> IGNORE;IGNORE;IGNORE;<(3)>
+<(4)> IGNORE;IGNORE;IGNORE;<(4)>
+<(5)> IGNORE;IGNORE;IGNORE;<(5)>
+<(6)> IGNORE;IGNORE;IGNORE;<(6)>
+<(7)> IGNORE;IGNORE;IGNORE;<(7)>
+<(8)> IGNORE;IGNORE;IGNORE;<(8)>
+<(9)> IGNORE;IGNORE;IGNORE;<(9)>
+<(10)> IGNORE;IGNORE;IGNORE;<(10)>
+<(11)> IGNORE;IGNORE;IGNORE;<(11)>
+<(12)> IGNORE;IGNORE;IGNORE;<(12)>
+<(13)> IGNORE;IGNORE;IGNORE;<(13)>
+<(14)> IGNORE;IGNORE;IGNORE;<(14)>
+<(15)> IGNORE;IGNORE;IGNORE;<(15)>
+<(16)> IGNORE;IGNORE;IGNORE;<(16)>
+<(17)> IGNORE;IGNORE;IGNORE;<(17)>
+<(18)> IGNORE;IGNORE;IGNORE;<(18)>
+<(19)> IGNORE;IGNORE;IGNORE;<(19)>
+<(20)> IGNORE;IGNORE;IGNORE;<(20)>
+<1.> IGNORE;IGNORE;IGNORE;<1.>
+<2.> IGNORE;IGNORE;IGNORE;<2.>
+<3.> IGNORE;IGNORE;IGNORE;<3.>
+<4.> IGNORE;IGNORE;IGNORE;<4.>
+<5.> IGNORE;IGNORE;IGNORE;<5.>
+<6.> IGNORE;IGNORE;IGNORE;<6.>
+<7.> IGNORE;IGNORE;IGNORE;<7.>
+<8.> IGNORE;IGNORE;IGNORE;<8.>
+<9.> IGNORE;IGNORE;IGNORE;<9.>
+<10.> IGNORE;IGNORE;IGNORE;<10.>
+<11.> IGNORE;IGNORE;IGNORE;<11.>
+<12.> IGNORE;IGNORE;IGNORE;<12.>
+<13.> IGNORE;IGNORE;IGNORE;<13.>
+<14.> IGNORE;IGNORE;IGNORE;<14.>
+<15.> IGNORE;IGNORE;IGNORE;<15.>
+<16.> IGNORE;IGNORE;IGNORE;<16.>
+<17.> IGNORE;IGNORE;IGNORE;<17.>
+<18.> IGNORE;IGNORE;IGNORE;<18.>
+<19.> IGNORE;IGNORE;IGNORE;<19.>
+<20.> IGNORE;IGNORE;IGNORE;<20.>
+<0-o> IGNORE;IGNORE;IGNORE;<0-o>
+<hh> IGNORE;IGNORE;IGNORE;<hh>
+<HH> IGNORE;IGNORE;IGNORE;<HH>
+<vv> IGNORE;IGNORE;IGNORE;<vv>
+<VV> IGNORE;IGNORE;IGNORE;<VV>
+<3-> IGNORE;IGNORE;IGNORE;<3->
+<3_> IGNORE;IGNORE;IGNORE;<3_>
+<3!> IGNORE;IGNORE;IGNORE;<3!>
+<3//> IGNORE;IGNORE;IGNORE;<3//>
+<4-> IGNORE;IGNORE;IGNORE;<4->
+<4_> IGNORE;IGNORE;IGNORE;<4_>
+<4!> IGNORE;IGNORE;IGNORE;<4!>
+<4//> IGNORE;IGNORE;IGNORE;<4//>
+<dr> IGNORE;IGNORE;IGNORE;<dr>
+<dR> IGNORE;IGNORE;IGNORE;<dR>
+<Dr> IGNORE;IGNORE;IGNORE;<Dr>
+<DR> IGNORE;IGNORE;IGNORE;<DR>
+<dl> IGNORE;IGNORE;IGNORE;<dl>
+<dL> IGNORE;IGNORE;IGNORE;<dL>
+<Dl> IGNORE;IGNORE;IGNORE;<Dl>
+<LD> IGNORE;IGNORE;IGNORE;<LD>
+<ur> IGNORE;IGNORE;IGNORE;<ur>
+<uR> IGNORE;IGNORE;IGNORE;<uR>
+<Ur> IGNORE;IGNORE;IGNORE;<Ur>
+<UR> IGNORE;IGNORE;IGNORE;<UR>
+<ul> IGNORE;IGNORE;IGNORE;<ul>
+<uL> IGNORE;IGNORE;IGNORE;<uL>
+<Ul> IGNORE;IGNORE;IGNORE;<Ul>
+<UL> IGNORE;IGNORE;IGNORE;<UL>
+<vr> IGNORE;IGNORE;IGNORE;<vr>
+<vR> IGNORE;IGNORE;IGNORE;<vR>
+<Udr> IGNORE;IGNORE;IGNORE;<Udr>
+<uDr> IGNORE;IGNORE;IGNORE;<uDr>
+<Vr> IGNORE;IGNORE;IGNORE;<Vr>
+<UdR> IGNORE;IGNORE;IGNORE;<UdR>
+<uDR> IGNORE;IGNORE;IGNORE;<uDR>
+<VR> IGNORE;IGNORE;IGNORE;<VR>
+<vl> IGNORE;IGNORE;IGNORE;<vl>
+<vL> IGNORE;IGNORE;IGNORE;<vL>
+<Udl> IGNORE;IGNORE;IGNORE;<Udl>
+<uDl> IGNORE;IGNORE;IGNORE;<uDl>
+<Vl> IGNORE;IGNORE;IGNORE;<Vl>
+<UdL> IGNORE;IGNORE;IGNORE;<UdL>
+<uDL> IGNORE;IGNORE;IGNORE;<uDL>
+<VL> IGNORE;IGNORE;IGNORE;<VL>
+<dh> IGNORE;IGNORE;IGNORE;<dh>
+<dLr> IGNORE;IGNORE;IGNORE;<dLr>
+<dlR> IGNORE;IGNORE;IGNORE;<dlR>
+<dH> IGNORE;IGNORE;IGNORE;<dH>
+<Dh> IGNORE;IGNORE;IGNORE;<Dh>
+<DLr> IGNORE;IGNORE;IGNORE;<DLr>
+<DlR> IGNORE;IGNORE;IGNORE;<DlR>
+<DH> IGNORE;IGNORE;IGNORE;<DH>
+<uh> IGNORE;IGNORE;IGNORE;<uh>
+<uLr> IGNORE;IGNORE;IGNORE;<uLr>
+<ulR> IGNORE;IGNORE;IGNORE;<ulR>
+<uH> IGNORE;IGNORE;IGNORE;<uH>
+<Uh> IGNORE;IGNORE;IGNORE;<Uh>
+<ULr> IGNORE;IGNORE;IGNORE;<ULr>
+<UlR> IGNORE;IGNORE;IGNORE;<UlR>
+<UH> IGNORE;IGNORE;IGNORE;<UH>
+<vh> IGNORE;IGNORE;IGNORE;<vh>
+<vLr> IGNORE;IGNORE;IGNORE;<vLr>
+<vlR> IGNORE;IGNORE;IGNORE;<vlR>
+<vH> IGNORE;IGNORE;IGNORE;<vH>
+<Udh> IGNORE;IGNORE;IGNORE;<Udh>
+<uDh> IGNORE;IGNORE;IGNORE;<uDh>
+<Vh> IGNORE;IGNORE;IGNORE;<Vh>
+<UdLr> IGNORE;IGNORE;IGNORE;<UdLr>
+<UdlR> IGNORE;IGNORE;IGNORE;<UdlR>
+<uDLr> IGNORE;IGNORE;IGNORE;<uDLr>
+<uDlR> IGNORE;IGNORE;IGNORE;<uDlR>
+<UdH> IGNORE;IGNORE;IGNORE;<UdH>
+<uDH> IGNORE;IGNORE;IGNORE;<uDH>
+<VLr> IGNORE;IGNORE;IGNORE;<VLr>
+<VlR> IGNORE;IGNORE;IGNORE;<VlR>
+<VH> IGNORE;IGNORE;IGNORE;<VH>
+<FD> IGNORE;IGNORE;IGNORE;<FD>
+<BD> IGNORE;IGNORE;IGNORE;<BD>
+<TB> IGNORE;IGNORE;IGNORE;<TB>
+<LB> IGNORE;IGNORE;IGNORE;<LB>
+<FB> IGNORE;IGNORE;IGNORE;<FB>
+<lB> IGNORE;IGNORE;IGNORE;<lB>
+<RB> IGNORE;IGNORE;IGNORE;<RB>
+<.S> IGNORE;IGNORE;IGNORE;<.S>
+<:S> IGNORE;IGNORE;IGNORE;<:S>
+<?S> IGNORE;IGNORE;IGNORE;<?S>
+<fS> IGNORE;IGNORE;IGNORE;<fS>
+<OS> IGNORE;IGNORE;IGNORE;<OS>
+<RO> IGNORE;IGNORE;IGNORE;<RO>
+<Rr> IGNORE;IGNORE;IGNORE;<Rr>
+<RF> IGNORE;IGNORE;IGNORE;<RF>
+<RY> IGNORE;IGNORE;IGNORE;<RY>
+<RH> IGNORE;IGNORE;IGNORE;<RH>
+<RZ> IGNORE;IGNORE;IGNORE;<RZ>
+<RK> IGNORE;IGNORE;IGNORE;<RK>
+<RX> IGNORE;IGNORE;IGNORE;<RX>
+<sB> IGNORE;IGNORE;IGNORE;<sB>
+<SR> IGNORE;IGNORE;IGNORE;<SR>
+<Or> IGNORE;IGNORE;IGNORE;<Or>
+<UT> IGNORE;IGNORE;IGNORE;<UT>
+<uT> IGNORE;IGNORE;IGNORE;<uT>
+<Tr> IGNORE;IGNORE;IGNORE;<Tr>
+<PR> IGNORE;IGNORE;IGNORE;<PR>
+<Dt> IGNORE;IGNORE;IGNORE;<Dt>
+<dT> IGNORE;IGNORE;IGNORE;<dT>
+<Tl> IGNORE;IGNORE;IGNORE;<Tl>
+<PL> IGNORE;IGNORE;IGNORE;<PL>
+<Db> IGNORE;IGNORE;IGNORE;<Db>
+<Dw> IGNORE;IGNORE;IGNORE;<Dw>
+<LZ> IGNORE;IGNORE;IGNORE;<LZ>
+<0m> IGNORE;IGNORE;IGNORE;<0m>
+<0o> IGNORE;IGNORE;IGNORE;<0o>
+<0M> IGNORE;IGNORE;IGNORE;<0M>
+<0L> IGNORE;IGNORE;IGNORE;<0L>
+<0R> IGNORE;IGNORE;IGNORE;<0R>
+<Sn> IGNORE;IGNORE;IGNORE;<Sn>
+<Ic> IGNORE;IGNORE;IGNORE;<Ic>
+<Fd> IGNORE;IGNORE;IGNORE;<Fd>
+<Bd> IGNORE;IGNORE;IGNORE;<Bd>
+<Ci> IGNORE;IGNORE;IGNORE;<Ci>
+<*2> IGNORE;IGNORE;IGNORE;<*2>
+<*1> IGNORE;IGNORE;IGNORE;<*1>
+<TEL> IGNORE;IGNORE;IGNORE;<TEL>
+<tel> IGNORE;IGNORE;IGNORE;<tel>
+<<H> IGNORE;IGNORE;IGNORE;<<H>
+</>H> IGNORE;IGNORE;IGNORE;</>H>
+<0u> IGNORE;IGNORE;IGNORE;<0u>
+<0U> IGNORE;IGNORE;IGNORE;<0U>
+<SU> IGNORE;IGNORE;IGNORE;<SU>
+<Fm> IGNORE;IGNORE;IGNORE;<Fm>
+<Ml> IGNORE;IGNORE;IGNORE;<Ml>
+<cS> IGNORE;IGNORE;IGNORE;<cS>
+<cH> IGNORE;IGNORE;IGNORE;<cH>
+<cD> IGNORE;IGNORE;IGNORE;<cD>
+<cC> IGNORE;IGNORE;IGNORE;<cC>
+<cS-> IGNORE;IGNORE;IGNORE;<cS->
+<cH-> IGNORE;IGNORE;IGNORE;<cH->
+<cD-> IGNORE;IGNORE;IGNORE;<cD->
+<cC-> IGNORE;IGNORE;IGNORE;<cC->
+<Md> IGNORE;IGNORE;IGNORE;<Md>
+<M8> IGNORE;IGNORE;IGNORE;<M8>
+<M2> IGNORE;IGNORE;IGNORE;<M2>
+<M16> IGNORE;IGNORE;IGNORE;<M16>
+<Mb> IGNORE;IGNORE;IGNORE;<Mb>
+<Mx> IGNORE;IGNORE;IGNORE;<Mx>
+<MX> IGNORE;IGNORE;IGNORE;<MX>
+<OK> IGNORE;IGNORE;IGNORE;<OK>
+<XX> IGNORE;IGNORE;IGNORE;<XX>
+<-X> IGNORE;IGNORE;IGNORE;<-X>
+<IS> IGNORE;IGNORE;IGNORE;<IS>
+<,_> IGNORE;IGNORE;IGNORE;<,_>
+<._> IGNORE;IGNORE;IGNORE;<._>
+<+"> IGNORE;IGNORE;IGNORE;<+">
+<JIS> IGNORE;IGNORE;IGNORE;<JIS>
+<*_> IGNORE;IGNORE;IGNORE;<*_>
+<;_> IGNORE;IGNORE;IGNORE;<;_>
+<0_> IGNORE;IGNORE;IGNORE;<0_>
+<<+> IGNORE;IGNORE;IGNORE;<<+>
+</>+> IGNORE;IGNORE;IGNORE;</>+>
+<<'> IGNORE;IGNORE;IGNORE;<<'>
+</>'> IGNORE;IGNORE;IGNORE;</>'>
+<<"> IGNORE;IGNORE;IGNORE;<<">
+</>"> IGNORE;IGNORE;IGNORE;</>">
+<("> IGNORE;IGNORE;IGNORE;<(">
+<)"> IGNORE;IGNORE;IGNORE;<)">
+<=T> IGNORE;IGNORE;IGNORE;<=T>
+<=_> IGNORE;IGNORE;IGNORE;<=_>
+<('> IGNORE;IGNORE;IGNORE;<('>
+<)'> IGNORE;IGNORE;IGNORE;<)'>
+<(I> IGNORE;IGNORE;IGNORE;<(I>
+<)I> IGNORE;IGNORE;IGNORE;<)I>
+<-?> IGNORE;IGNORE;IGNORE;<-?>
+<=T:)> IGNORE;IGNORE;IGNORE;<=T:)>
+<"5> IGNORE;IGNORE;IGNORE;<"5>
+<05> IGNORE;IGNORE;IGNORE;<05>
+<*5> IGNORE;IGNORE;IGNORE;<*5>
+<+5> IGNORE;IGNORE;IGNORE;<+5>
+<.6> IGNORE;IGNORE;IGNORE;<.6>
+<-6> IGNORE;IGNORE;IGNORE;<-6>
+<*6> IGNORE;IGNORE;IGNORE;<*6>
+<+6> IGNORE;IGNORE;IGNORE;<+6>
+<(JU)> IGNORE;IGNORE;IGNORE;<(JU)>
+<1c> IGNORE;IGNORE;IGNORE;<1c>
+<2c> IGNORE;IGNORE;IGNORE;<2c>
+<3c> IGNORE;IGNORE;IGNORE;<3c>
+<4c> IGNORE;IGNORE;IGNORE;<4c>
+<5c> IGNORE;IGNORE;IGNORE;<5c>
+<6c> IGNORE;IGNORE;IGNORE;<6c>
+<7c> IGNORE;IGNORE;IGNORE;<7c>
+<8c> IGNORE;IGNORE;IGNORE;<8c>
+<9c> IGNORE;IGNORE;IGNORE;<9c>
+<10c> IGNORE;IGNORE;IGNORE;<10c>
+<KSC> IGNORE;IGNORE;IGNORE;<KSC>
+<am> IGNORE;IGNORE;IGNORE;<am>
+<pm> IGNORE;IGNORE;IGNORE;<pm>
+<NU> IGNORE;IGNORE;IGNORE;<NU>
+<SH> IGNORE;IGNORE;IGNORE;<SH>
+<SX> IGNORE;IGNORE;IGNORE;<SX>
+<EX> IGNORE;IGNORE;IGNORE;<EX>
+<ET> IGNORE;IGNORE;IGNORE;<ET>
+<EQ> IGNORE;IGNORE;IGNORE;<EQ>
+<AK> IGNORE;IGNORE;IGNORE;<AK>
+<BL> IGNORE;IGNORE;IGNORE;<BL>
+<BS> IGNORE;IGNORE;IGNORE;<BS>
+<SO> IGNORE;IGNORE;IGNORE;<SO>
+<SI> IGNORE;IGNORE;IGNORE;<SI>
+<DL> IGNORE;IGNORE;IGNORE;<DL>
+<D1> IGNORE;IGNORE;IGNORE;<D1>
+<D2> IGNORE;IGNORE;IGNORE;<D2>
+<D3> IGNORE;IGNORE;IGNORE;<D3>
+<D4> IGNORE;IGNORE;IGNORE;<D4>
+<NK> IGNORE;IGNORE;IGNORE;<NK>
+<SY> IGNORE;IGNORE;IGNORE;<SY>
+<EB> IGNORE;IGNORE;IGNORE;<EB>
+<CN> IGNORE;IGNORE;IGNORE;<CN>
+<EM> IGNORE;IGNORE;IGNORE;<EM>
+<SB> IGNORE;IGNORE;IGNORE;<SB>
+<EC> IGNORE;IGNORE;IGNORE;<EC>
+<FS> IGNORE;IGNORE;IGNORE;<FS>
+<GS> IGNORE;IGNORE;IGNORE;<GS>
+<RS> IGNORE;IGNORE;IGNORE;<RS>
+<US> IGNORE;IGNORE;IGNORE;<US>
+<DT> IGNORE;IGNORE;IGNORE;<DT>
+<PA> IGNORE;IGNORE;IGNORE;<PA>
+<HO> IGNORE;IGNORE;IGNORE;<HO>
+<BH> IGNORE;IGNORE;IGNORE;<BH>
+<NH> IGNORE;IGNORE;IGNORE;<NH>
+<IN> IGNORE;IGNORE;IGNORE;<IN>
+<NL> IGNORE;IGNORE;IGNORE;<NL>
+<SA> IGNORE;IGNORE;IGNORE;<SA>
+<ES> IGNORE;IGNORE;IGNORE;<ES>
+<HS> IGNORE;IGNORE;IGNORE;<HS>
+<HJ> IGNORE;IGNORE;IGNORE;<HJ>
+<VS> IGNORE;IGNORE;IGNORE;<VS>
+<PD> IGNORE;IGNORE;IGNORE;<PD>
+<PU> IGNORE;IGNORE;IGNORE;<PU>
+<RI> IGNORE;IGNORE;IGNORE;<RI>
+<S2> IGNORE;IGNORE;IGNORE;<S2>
+<S3> IGNORE;IGNORE;IGNORE;<S3>
+<DC> IGNORE;IGNORE;IGNORE;<DC>
+<P1> IGNORE;IGNORE;IGNORE;<P1>
+<P2> IGNORE;IGNORE;IGNORE;<P2>
+<TS> IGNORE;IGNORE;IGNORE;<TS>
+<CC> IGNORE;IGNORE;IGNORE;<CC>
+<MW> IGNORE;IGNORE;IGNORE;<MW>
+<SG> IGNORE;IGNORE;IGNORE;<SG>
+<EG> IGNORE;IGNORE;IGNORE;<EG>
+<SS> IGNORE;IGNORE;IGNORE;<SS>
+<GC> IGNORE;IGNORE;IGNORE;<GC>
+<SC> IGNORE;IGNORE;IGNORE;<SC>
+<CI> IGNORE;IGNORE;IGNORE;<CI>
+<ST> IGNORE;IGNORE;IGNORE;<ST>
+<OC> IGNORE;IGNORE;IGNORE;<OC>
+<PM> IGNORE;IGNORE;IGNORE;<PM>
+<AC> IGNORE;IGNORE;IGNORE;<AC>
+<"3> IGNORE;IGNORE;IGNORE;<"3>
+<"1> IGNORE;IGNORE;IGNORE;<"1>
+<"!> IGNORE;IGNORE;IGNORE;<"!>
+<"'> IGNORE;IGNORE;IGNORE;<"'>
+<"/>> IGNORE;IGNORE;IGNORE;<"/>>
+<"?> IGNORE;IGNORE;IGNORE;<"?>
+<"-> IGNORE;IGNORE;IGNORE;<"->
+<"(> IGNORE;IGNORE;IGNORE;<"(>
+<".> IGNORE;IGNORE;IGNORE;<".>
+<":> IGNORE;IGNORE;IGNORE;<":>
+<"0> IGNORE;IGNORE;IGNORE;<"0>
+<",> IGNORE;IGNORE;IGNORE;<",>
+<"_> IGNORE;IGNORE;IGNORE;<"_>
+<""> IGNORE;IGNORE;IGNORE;<"">
+<";> IGNORE;IGNORE;IGNORE;<";>
+<"<> IGNORE;IGNORE;IGNORE;<"<>
+<"=> IGNORE;IGNORE;IGNORE;<"=>
+<"//> IGNORE;IGNORE;IGNORE;<"//>
+<"p> IGNORE;IGNORE;IGNORE;<"p>
+<"d> IGNORE;IGNORE;IGNORE;<"d>
+<"i> IGNORE;IGNORE;IGNORE;<"i>
+<+_> IGNORE;IGNORE;IGNORE;<+_>
+<Tel> IGNORE;IGNORE;IGNORE;<Tel>
+<UA> IGNORE;IGNORE;IGNORE;<UA>
+<UB> IGNORE;IGNORE;IGNORE;<UB>
+UNDEFINED IGNORE;IGNORE;IGNORE
+
+<0> <0>;<0>;IGNORE;IGNORE
+<0S> <0>;<0S>;IGNORE;IGNORE
+<18> <0>;<18>;IGNORE;IGNORE
+<14> <0>;<14>;IGNORE;IGNORE
+<38> <0>;<38>;IGNORE;IGNORE
+<12> <0>;<12>;IGNORE;IGNORE
+<58> <0>;<58>;IGNORE;IGNORE
+<34> <0>;<34>;IGNORE;IGNORE
+<78> <0>;<78>;IGNORE;IGNORE
+<1> <1>;<1>;IGNORE;IGNORE
+<2> <2>;<2>;IGNORE;IGNORE
+<3> <3>;<3>;IGNORE;IGNORE
+<4> <4>;<4>;IGNORE;IGNORE
+<5> <5>;<5>;IGNORE;IGNORE
+<6> <6>;<6>;IGNORE;IGNORE
+<7> <7>;<7>;IGNORE;IGNORE
+<8> <8>;<8>;IGNORE;IGNORE
+<9> <9>;<9>;IGNORE;IGNORE
+<1S> <1>;<1S>;IGNORE;IGNORE
+<2S> <2>;<2S>;IGNORE;IGNORE
+<3S> <3>;<3S>;IGNORE;IGNORE
+<4S> <4>;<4S>;IGNORE;IGNORE
+<5S> <5>;<5S>;IGNORE;IGNORE
+<6S> <6>;<6S>;IGNORE;IGNORE
+<7S> <7>;<7S>;IGNORE;IGNORE
+<8S> <8>;<8S>;IGNORE;IGNORE
+<9S> <9>;<9S>;IGNORE;IGNORE
+<A> <A>;<NONE>;<CAPITAL>;IGNORE
+<a> <A>;<NONE>;<SMALL>;IGNORE
+<-a> <A>;<NONE>;<-a>;IGNORE
+<A'> <A>;<ACUTE>;<CAPITAL>;IGNORE
+<a'> <A>;<ACUTE>;<SMALL>;IGNORE
+<A!> <A>;<GRAVE>;<CAPITAL>;IGNORE
+<a!> <A>;<GRAVE>;<SMALL>;IGNORE
+<A!!> <A>;<DOUBLE-GRAVE>;<CAPITAL>;IGNORE
+<a!!> <A>;<DOUBLE-GRAVE>;<SMALL>;IGNORE
+<A(> <A>;<BREVE>;<CAPITAL>;IGNORE
+<a(> <A>;<BREVE>;<SMALL>;IGNORE
+<A('> <A>;<BREVE+ACUTE>;<CAPITAL>;IGNORE
+<a('> <A>;<BREVE+ACUTE>;<SMALL>;IGNORE
+<A(!> <A>;<BREVE+GRAVE>;<CAPITAL>;IGNORE
+<a(!> <A>;<BREVE+GRAVE>;<SMALL>;IGNORE
+<A(2> <A>;<BREVE+HOOK>;<CAPITAL>;IGNORE
+<a(2> <A>;<BREVE+HOOK>;<SMALL>;IGNORE
+<A(?> <A>;<BREVE+TILDE>;<CAPITAL>;IGNORE
+<a(?> <A>;<BREVE+TILDE>;<SMALL>;IGNORE
+<A(-.> <A>;<BREVE+DOT-BELOW>;<CAPITAL>;IGNORE
+<a(-.> <A>;<BREVE+DOT-BELOW>;<SMALL>;IGNORE
+<A)> <A>;<INVERTED-BREVE>;<CAPITAL>;IGNORE
+<a)> <A>;<INVERTED-BREVE>;<SMALL>;IGNORE
+<A/>> <A>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<a/>> <A>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<A/>'> <A>;<CIRCUMFLEX+ACUTE>;<CAPITAL>;IGNORE
+<a/>'> <A>;<CIRCUMFLEX+ACUTE>;<SMALL>;IGNORE
+<A/>!> <A>;<CIRCUMFLEX+GRAVE>;<CAPITAL>;IGNORE
+<a/>!> <A>;<CIRCUMFLEX+GRAVE>;<SMALL>;IGNORE
+<A/>2> <A>;<CIRCUMFLEX+HOOK>;<CAPITAL>;IGNORE
+<a/>2> <A>;<CIRCUMFLEX+HOOK>;<SMALL>;IGNORE
+<A/>?> <A>;<CIRCUMFLEX+TILDE>;<CAPITAL>;IGNORE
+<a/>?> <A>;<CIRCUMFLEX+TILDE>;<SMALL>;IGNORE
+<A/>-.> <A>;<CIRCUMFLEX+DOT-BELOW>;<CAPITAL>;IGNORE
+<a/>-.> <A>;<CIRCUMFLEX+DOT-BELOW>;<SMALL>;IGNORE
+<A<> <A>;<CARON>;<CAPITAL>;IGNORE
+<a<> <A>;<CARON>;<SMALL>;IGNORE
+<AA> <A>;<RING>;<CAPITAL>;IGNORE
+<aa> <A>;<RING>;<SMALL>;IGNORE
+<AA'> <A>;<RING+ACUTE>;<CAPITAL>;IGNORE
+<aa'> <A>;<RING+ACUTE>;<SMALL>;IGNORE
+<A-0> <A>;<RING-BELOW>;<CAPITAL>;IGNORE
+<a-0> <A>;<RING-BELOW>;<SMALL>;IGNORE
+<A:> <A>;<DIAERESIS>;<CAPITAL>;IGNORE
+<a:> <A>;<DIAERESIS>;<SMALL>;IGNORE
+<A1> <A>;<DIAERESIS+MACRON>;<CAPITAL>;IGNORE
+<a1> <A>;<DIAERESIS+MACRON>;<SMALL>;IGNORE
+<A2> <A>;<HOOK>;<CAPITAL>;IGNORE
+<a2> <A>;<HOOK>;<SMALL>;IGNORE
+<A?> <A>;<TILDE>;<CAPITAL>;IGNORE
+<a?> <A>;<TILDE>;<SMALL>;IGNORE
+<A-.> <A>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<a-.> <A>;<DOT-BELOW>;<SMALL>;IGNORE
+<A;> <A>;<OGONEK>;<CAPITAL>;IGNORE
+<a;> <A>;<OGONEK>;<SMALL>;IGNORE
+<A-> <A>;<MACRON>;<CAPITAL>;IGNORE
+<a-> <A>;<MACRON>;<SMALL>;IGNORE
+<A7> <A>;<MACRON+DOT>;<CAPITAL>;IGNORE
+<a7> <A>;<MACRON+DOT>;<SMALL>;IGNORE
+<a8>
+<AE> "<A><E>";"<AE><AE>";"<CAPITAL><CAPITAL>";IGNORE
+<ae> "<A><E>";"<AE><AE>";"<SMALL><SMALL>";IGNORE
+<AE'> "<A><E>";"<AE'><AE'>";"<CAPITAL><CAPITAL>";IGNORE
+<ae'> "<A><E>";"<AE'><AE'>";"<SMALL><SMALL>";IGNORE
+<A3> "<A><E>";"<A3><A3>";"<CAPITAL><CAPITAL>";IGNORE
+<a3> "<A><E>";"<A3><A3>";"<SMALL><SMALL>";IGNORE
+<B> <B>;<NONE>;<CAPITAL>;IGNORE
+<b> <B>;<NONE>;<SMALL>;IGNORE
+<B.> <B>;<DOT>;<CAPITAL>;IGNORE
+<b.> <B>;<DOT>;<SMALL>;IGNORE
+<B-.> <B>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<b-.> <B>;<DOT-BELOW>;<SMALL>;IGNORE
+<B_> <B>;<LINE-BELOW>;<CAPITAL>;IGNORE
+<b_> <B>;<LINE-BELOW>;<SMALL>;IGNORE
+<b8>
+<C> <C>;<NONE>;<CAPITAL>;IGNORE
+<c> <C>;<NONE>;<SMALL>;IGNORE
+<C'> <C>;<ACUTE>;<CAPITAL>;IGNORE
+<c'> <C>;<ACUTE>;<SMALL>;IGNORE
+<C/>> <C>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<c/>> <C>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<C<> <C>;<CARON>;<CAPITAL>;IGNORE
+<c<> <C>;<CARON>;<SMALL>;IGNORE
+<C2> <C>;<HOOK>;<CAPITAL>;IGNORE
+<c2> <C>;<HOOK>;<SMALL>;IGNORE
+<C.> <C>;<DOT>;<CAPITAL>;IGNORE
+<c.> <C>;<DOT>;<SMALL>;IGNORE
+<C,> <C>;<CEDILLA>;<CAPITAL>;IGNORE
+<c,> <C>;<CEDILLA>;<SMALL>;IGNORE
+<C,'> <C>;<CEDILLA+ACUTE>;<CAPITAL>;IGNORE
+<c,'> <C>;<CEDILLA+ACUTE>;<SMALL>;IGNORE
+<c8>
+<D> <D>;<NONE>;<CAPITAL>;IGNORE
+<d> <D>;<NONE>;<SMALL>;IGNORE
+<D<> <D>;<CARON>;<CAPITAL>;IGNORE
+<d<> <D>;<CARON>;<SMALL>;IGNORE
+<D.> <D>;<DOT>;<CAPITAL>;IGNORE
+<d.> <D>;<DOT>;<SMALL>;IGNORE
+<D-.> <D>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<d-.> <D>;<DOT-BELOW>;<SMALL>;IGNORE
+<D//> <D>;<STROKE>;<CAPITAL>;IGNORE
+<d//> <D>;<STROKE>;<SMALL>;IGNORE
+<D,> <D>;<CEDILLA>;<CAPITAL>;IGNORE
+<d,> <D>;<CEDILLA>;<SMALL>;IGNORE
+<D-/>> <D>;<MACRON+CIRCUMFLEX>;<CAPITAL>;IGNORE
+<d-/>> <D>;<MACRON+CIRCUMFLEX>;<SMALL>;IGNORE
+<D_> <D>;<LINE-BELOW>;<CAPITAL>;IGNORE
+<d_> <D>;<LINE-BELOW>;<SMALL>;IGNORE
+<d8>
+<E> <E>;<NONE>;<CAPITAL>;IGNORE
+<e> <E>;<NONE>;<SMALL>;IGNORE
+<E'> <E>;<ACUTE>;<CAPITAL>;IGNORE
+<e'> <E>;<ACUTE>;<SMALL>;IGNORE
+<E!> <E>;<GRAVE>;<CAPITAL>;IGNORE
+<e!> <E>;<GRAVE>;<SMALL>;IGNORE
+<E!!> <E>;<DOUBLE-GRAVE>;<CAPITAL>;IGNORE
+<e!!> <E>;<DOUBLE-GRAVE>;<SMALL>;IGNORE
+<E(> <E>;<BREVE>;<CAPITAL>;IGNORE
+<e(> <E>;<BREVE>;<SMALL>;IGNORE
+<E)> <E>;<INVERTED-BREVE>;<CAPITAL>;IGNORE
+<e)> <E>;<INVERTED-BREVE>;<SMALL>;IGNORE
+<E/>> <E>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<e/>> <E>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<E/>'> <E>;<CIRCUMFLEX+ACUTE>;<CAPITAL>;IGNORE
+<e/>'> <E>;<CIRCUMFLEX+ACUTE>;<SMALL>;IGNORE
+<E/>!> <E>;<CIRCUMFLEX+GRAVE>;<CAPITAL>;IGNORE
+<e/>!> <E>;<CIRCUMFLEX+GRAVE>;<SMALL>;IGNORE
+<E/>2> <E>;<CIRCUMFLEX+HOOK>;<CAPITAL>;IGNORE
+<e/>2> <E>;<CIRCUMFLEX+HOOK>;<SMALL>;IGNORE
+<E/>?> <E>;<CIRCUMFLEX+TILDE>;<CAPITAL>;IGNORE
+<e/>?> <E>;<CIRCUMFLEX+TILDE>;<SMALL>;IGNORE
+<E/>-.> <E>;<CIRCUMFLEX+DOT-BELOW>;<CAPITAL>;IGNORE
+<e/>-.> <E>;<CIRCUMFLEX+DOT-BELOW>;<SMALL>;IGNORE
+<E<> <E>;<CARON>;<CAPITAL>;IGNORE
+<e<> <E>;<CARON>;<SMALL>;IGNORE
+<E:> <E>;<DIAERESIS>;<CAPITAL>;IGNORE
+<e:> <E>;<DIAERESIS>;<SMALL>;IGNORE
+<E2> <E>;<HOOK>;<CAPITAL>;IGNORE
+<e2> <E>;<HOOK>;<SMALL>;IGNORE
+<E?> <E>;<TILDE>;<CAPITAL>;IGNORE
+<e?> <E>;<TILDE>;<SMALL>;IGNORE
+<E-?> <E>;<TILDE-BELOW>;<CAPITAL>;IGNORE
+<e-?> <E>;<TILDE-BELOW>;<SMALL>;IGNORE
+<E.> <E>;<DOT>;<CAPITAL>;IGNORE
+<e.> <E>;<DOT>;<SMALL>;IGNORE
+<E-.> <E>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<e-.> <E>;<DOT-BELOW>;<SMALL>;IGNORE
+<E,(> <E>;<CEDILLA+BREVE>;<CAPITAL>;IGNORE
+<e,(> <E>;<CEDILLA+BREVE>;<SMALL>;IGNORE
+<E;> <E>;<OGONEK>;<CAPITAL>;IGNORE
+<e;> <E>;<OGONEK>;<SMALL>;IGNORE
+<E-> <E>;<MACRON>;<CAPITAL>;IGNORE
+<e-> <E>;<MACRON>;<SMALL>;IGNORE
+<E-'> <E>;<MACRON+ACUTE>;<CAPITAL>;IGNORE
+<e-'> <E>;<MACRON+ACUTE>;<SMALL>;IGNORE
+<E-!> <E>;<MACRON+GRAVE>;<CAPITAL>;IGNORE
+<e-!> <E>;<MACRON+GRAVE>;<SMALL>;IGNORE
+<E-/>> <E>;<MACRON+CIRCUMFLEX>;<CAPITAL>;IGNORE
+<e-/>> <E>;<MACRON+CIRCUMFLEX>;<SMALL>;IGNORE
+<e8>
+<F> <F>;<NONE>;<CAPITAL>;IGNORE
+<f> <F>;<NONE>;<SMALL>;IGNORE
+<F2> <F>;<HOOK>;<CAPITAL>;IGNORE
+<f2> <F>;<HOOK>;<SMALL>;IGNORE
+<F.> <F>;<DOT>;<CAPITAL>;IGNORE
+<f.> <F>;<DOT>;<SMALL>;IGNORE
+<f8>
+<ff> "<F><F>";"<NONE><NONE>";"<ff><ff>";IGNORE
+<fi> "<F><I>";"<NONE><NONE>";"<fi><fi>";IGNORE
+<fl> "<F><L>";"<NONE><NONE>";"<fl><fl>";IGNORE
+<ffi> "<F><F><I>";"<NONE><NONE><NONE>";"<ffi><ffi><ffi>";IGNORE
+<ffl> "<F><F><L>";"<NONE><NONE><NONE>";"<ffl><ffl><ffl>";IGNORE
+<ft> "<F><T>";"<NONE><NONE>";"<ft><ft>";IGNORE
+<G> <G>;<NONE>;<CAPITAL>;IGNORE
+<g> <G>;<NONE>;<SMALL>;IGNORE
+<G'> <G>;<ACUTE>;<CAPITAL>;IGNORE
+<g'> <G>;<ACUTE>;<SMALL>;IGNORE
+<G(> <G>;<BREVE>;<CAPITAL>;IGNORE
+<g(> <G>;<BREVE>;<SMALL>;IGNORE
+<G/>> <G>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<g/>> <G>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<G<> <G>;<CARON>;<CAPITAL>;IGNORE
+<g<> <G>;<CARON>;<SMALL>;IGNORE
+<G.> <G>;<DOT>;<CAPITAL>;IGNORE
+<g.> <G>;<DOT>;<SMALL>;IGNORE
+<G//> <G>;<STROKE>;<CAPITAL>;IGNORE
+<g//> <G>;<STROKE>;<SMALL>;IGNORE
+<G,> <G>;<CEDILLA>;<CAPITAL>;IGNORE
+<g,> <G>;<CEDILLA>;<SMALL>;IGNORE
+<G-> <G>;<MACRON>;<CAPITAL>;IGNORE
+<g-> <G>;<MACRON>;<SMALL>;IGNORE
+<g8>
+<H> <H>;<NONE>;<CAPITAL>;IGNORE
+<h> <H>;<NONE>;<SMALL>;IGNORE
+<H-(> <H>;<BREVE-BELOW>;<CAPITAL>;IGNORE
+<h-(> <H>;<BREVE-BELOW>;<SMALL>;IGNORE
+<H/>> <H>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<h/>> <H>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<H:> <H>;<DIAERESIS>;<CAPITAL>;IGNORE
+<h:> <H>;<DIAERESIS>;<SMALL>;IGNORE
+<H.> <H>;<DOT>;<CAPITAL>;IGNORE
+<h.> <H>;<DOT>;<SMALL>;IGNORE
+<H-.> <H>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<h-.> <H>;<DOT-BELOW>;<SMALL>;IGNORE
+<H//> <H>;<STROKE>;<CAPITAL>;IGNORE
+<h//> <H>;<STROKE>;<SMALL>;IGNORE
+<H,> <H>;<CEDILLA>;<CAPITAL>;IGNORE
+<h,> <H>;<CEDILLA>;<SMALL>;IGNORE
+<h8>
+<I> <I>;<NONE>;<CAPITAL>;IGNORE
+<i> <I>;<NONE>;<SMALL>;IGNORE
+<I'> <I>;<ACUTE>;<CAPITAL>;IGNORE
+<i'> <I>;<ACUTE>;<SMALL>;IGNORE
+<I!> <I>;<GRAVE>;<CAPITAL>;IGNORE
+<i!> <I>;<GRAVE>;<SMALL>;IGNORE
+<I!!> <I>;<DOUBLE-GRAVE>;<CAPITAL>;IGNORE
+<i!!> <I>;<DOUBLE-GRAVE>;<SMALL>;IGNORE
+<I(> <I>;<BREVE>;<CAPITAL>;IGNORE
+<i(> <I>;<BREVE>;<SMALL>;IGNORE
+<I)> <I>;<INVERTED-BREVE>;<CAPITAL>;IGNORE
+<i)> <I>;<INVERTED-BREVE>;<SMALL>;IGNORE
+<I/>> <I>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<i/>> <I>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<I<> <I>;<CARON>;<CAPITAL>;IGNORE
+<i<> <I>;<CARON>;<SMALL>;IGNORE
+<I:> <I>;<DIAERESIS>;<CAPITAL>;IGNORE
+<i:> <I>;<DIAERESIS>;<SMALL>;IGNORE
+<I:'> <I>;<DIAERESIS+ACUTE>;<CAPITAL>;IGNORE
+<i:'> <I>;<DIAERESIS+ACUTE>;<SMALL>;IGNORE
+<I2> <I>;<HOOK>;<CAPITAL>;IGNORE
+<i2> <I>;<HOOK>;<SMALL>;IGNORE
+<I?> <I>;<TILDE>;<CAPITAL>;IGNORE
+<i?> <I>;<TILDE>;<SMALL>;IGNORE
+<I-?> <I>;<TILDE-BELOW>;<CAPITAL>;IGNORE
+<i-?> <I>;<TILDE-BELOW>;<SMALL>;IGNORE
+<I.> <I>;<DOT>;<CAPITAL>;IGNORE
+<i.> <I>;<DOT>;<SMALL>;IGNORE
+<I-.> <I>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<i-.> <I>;<DOT-BELOW>;<SMALL>;IGNORE
+<I;> <I>;<OGONEK>;<CAPITAL>;IGNORE
+<i;> <I>;<OGONEK>;<SMALL>;IGNORE
+<I-> <I>;<MACRON>;<CAPITAL>;IGNORE
+<i-> <I>;<MACRON>;<SMALL>;IGNORE
+<i8>
+<IJ> "<I><J>";"<IJ><IJ>";"<CAPITAL><CAPITAL>";IGNORE
+<ij> "<I><J>";"<IJ><IJ>";"<SMALL><SMALL>";IGNORE
+<J> <J>;<NONE>;<CAPITAL>;IGNORE
+<j> <J>;<NONE>;<SMALL>;IGNORE
+<J/>> <J>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<j/>> <J>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<j8>
+<K> <K>;<NONE>;<CAPITAL>;IGNORE
+<k> <K>;<NONE>;<SMALL>;IGNORE
+<K'> <K>;<ACUTE>;<CAPITAL>;IGNORE
+<k'> <K>;<ACUTE>;<SMALL>;IGNORE
+<K<> <K>;<CARON>;<CAPITAL>;IGNORE
+<k<> <K>;<CARON>;<SMALL>;IGNORE
+<K2> <K>;<HOOK>;<CAPITAL>;IGNORE
+<k2> <K>;<HOOK>;<SMALL>;IGNORE
+<K-.> <K>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<k-.> <K>;<DOT-BELOW>;<SMALL>;IGNORE
+<K,> <K>;<CEDILLA>;<CAPITAL>;IGNORE
+<k,> <K>;<CEDILLA>;<SMALL>;IGNORE
+<K_> <K>;<LINE-BELOW>;<CAPITAL>;IGNORE
+<k_> <K>;<LINE-BELOW>;<SMALL>;IGNORE
+<k8>
+<kk> <K>;<kk>;<SMALL>;IGNORE
+<L> <L>;<NONE>;<CAPITAL>;IGNORE
+<l> <L>;<NONE>;<SMALL>;IGNORE
+<L'> <L>;<ACUTE>;<CAPITAL>;IGNORE
+<l'> <L>;<ACUTE>;<SMALL>;IGNORE
+<L<> <L>;<CARON>;<CAPITAL>;IGNORE
+<l<> <L>;<CARON>;<SMALL>;IGNORE
+<L.> <L>;<DOT>;<CAPITAL>;IGNORE
+<l.> <L>;<DOT>;<SMALL>;IGNORE
+<L-.> <L>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<l-.> <L>;<DOT-BELOW>;<SMALL>;IGNORE
+<L//> <L>;<STROKE>;<CAPITAL>;IGNORE
+<l//> <L>;<STROKE>;<SMALL>;IGNORE
+<L,> <L>;<CEDILLA>;<CAPITAL>;IGNORE
+<l,> <L>;<CEDILLA>;<SMALL>;IGNORE
+<L--.> <L>;<MACRON+DOT-BELOW>;<CAPITAL>;IGNORE
+<l--.> <L>;<MACRON+DOT-BELOW>;<SMALL>;IGNORE
+<L-/>> <L>;<MACRON+CIRCUMFLEX>;<CAPITAL>;IGNORE
+<l-/>> <L>;<MACRON+CIRCUMFLEX>;<SMALL>;IGNORE
+<L_> <L>;<LINE-BELOW>;<CAPITAL>;IGNORE
+<l_> <L>;<LINE-BELOW>;<SMALL>;IGNORE
+<l8>
+<M> <M>;<NONE>;<CAPITAL>;IGNORE
+<m> <M>;<NONE>;<SMALL>;IGNORE
+<M'> <M>;<ACUTE>;<CAPITAL>;IGNORE
+<m'> <M>;<ACUTE>;<SMALL>;IGNORE
+<M.> <M>;<DOT>;<CAPITAL>;IGNORE
+<m.> <M>;<DOT>;<SMALL>;IGNORE
+<M-.> <M>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<m-.> <M>;<DOT-BELOW>;<SMALL>;IGNORE
+<m8>
+<N> <N>;<NONE>;<CAPITAL>;IGNORE
+<n> <N>;<NONE>;<SMALL>;IGNORE
+<N'> <N>;<ACUTE>;<CAPITAL>;IGNORE
+<n'> <N>;<ACUTE>;<SMALL>;IGNORE
+<N<> <N>;<CARON>;<CAPITAL>;IGNORE
+<n<> <N>;<CARON>;<SMALL>;IGNORE
+<N?> <N>;<TILDE>;<CAPITAL>;IGNORE
+<n?> <N>;<TILDE>;<SMALL>;IGNORE
+<N.> <N>;<DOT>;<CAPITAL>;IGNORE
+<n.> <N>;<DOT>;<SMALL>;IGNORE
+<N-.> <N>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<n-.> <N>;<DOT-BELOW>;<SMALL>;IGNORE
+<N,> <N>;<CEDILLA>;<CAPITAL>;IGNORE
+<n,> <N>;<CEDILLA>;<SMALL>;IGNORE
+<N-/>> <N>;<MACRON+CIRCUMFLEX>;<CAPITAL>;IGNORE
+<n-/>> <N>;<MACRON+CIRCUMFLEX>;<SMALL>;IGNORE
+<N_> <N>;<LINE-BELOW>;<CAPITAL>;IGNORE
+<n_> <N>;<LINE-BELOW>;<SMALL>;IGNORE
+<'n> <N>;<PRECEDED-BY-APOSTROPHE>;<SMALL>;IGNORE
+<n8>
+<NG> "<N><G>";"<NG><NG>";"<CAPITAL><CAPITAL>";IGNORE
+<ng> "<N><G>";"<NG><NG>";"<SMALL><SMALL>";IGNORE
+<O> <O>;<NONE>;<CAPITAL>;IGNORE
+<o> <O>;<NONE>;<SMALL>;IGNORE
+<-o> <O>;<NONE>;<-o>;IGNORE
+<O'> <O>;<ACUTE>;<CAPITAL>;IGNORE
+<o'> <O>;<ACUTE>;<SMALL>;IGNORE
+<O!> <O>;<GRAVE>;<CAPITAL>;IGNORE
+<o!> <O>;<GRAVE>;<SMALL>;IGNORE
+<O!!> <O>;<DOUBLE-GRAVE>;<CAPITAL>;IGNORE
+<o!!> <O>;<DOUBLE-GRAVE>;<SMALL>;IGNORE
+<O(> <O>;<BREVE>;<CAPITAL>;IGNORE
+<o(> <O>;<BREVE>;<SMALL>;IGNORE
+<O)> <O>;<INVERTED-BREVE>;<CAPITAL>;IGNORE
+<o)> <O>;<INVERTED-BREVE>;<SMALL>;IGNORE
+<O/>> <O>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<o/>> <O>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<O/>'> <O>;<CIRCUMFLEX+ACUTE>;<CAPITAL>;IGNORE
+<o/>'> <O>;<CIRCUMFLEX+ACUTE>;<SMALL>;IGNORE
+<O/>!> <O>;<CIRCUMFLEX+GRAVE>;<CAPITAL>;IGNORE
+<o/>!> <O>;<CIRCUMFLEX+GRAVE>;<SMALL>;IGNORE
+<O/>2> <O>;<CIRCUMFLEX+HOOK>;<CAPITAL>;IGNORE
+<o/>2> <O>;<CIRCUMFLEX+HOOK>;<SMALL>;IGNORE
+<O/>?> <O>;<CIRCUMFLEX+TILDE>;<CAPITAL>;IGNORE
+<o/>?> <O>;<CIRCUMFLEX+TILDE>;<SMALL>;IGNORE
+<O/>-.> <O>;<CIRCUMFLEX+DOT-BELOW>;<CAPITAL>;IGNORE
+<o/>-.> <O>;<CIRCUMFLEX+DOT-BELOW>;<SMALL>;IGNORE
+<O<> <O>;<CARON>;<CAPITAL>;IGNORE
+<o<> <O>;<CARON>;<SMALL>;IGNORE
+<O:> <O>;<DIAERESIS>;<CAPITAL>;IGNORE
+<o:> <O>;<DIAERESIS>;<SMALL>;IGNORE
+<O"> <O>;<DOUBLE-ACUTE>;<CAPITAL>;IGNORE
+<o"> <O>;<DOUBLE-ACUTE>;<SMALL>;IGNORE
+<O2> <O>;<HOOK>;<CAPITAL>;IGNORE
+<o2> <O>;<HOOK>;<SMALL>;IGNORE
+<O?> <O>;<TILDE>;<CAPITAL>;IGNORE
+<o?> <O>;<TILDE>;<SMALL>;IGNORE
+<O?'> <O>;<TILDE+ACUTE>;<CAPITAL>;IGNORE
+<o?'> <O>;<TILDE+ACUTE>;<SMALL>;IGNORE
+<O?:> <O>;<TILDE+DIAERESIS>;<CAPITAL>;IGNORE
+<o?:> <O>;<TILDE+DIAERESIS>;<SMALL>;IGNORE
+<O-.> <O>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<o-.> <O>;<DOT-BELOW>;<SMALL>;IGNORE
+<O//> <O>;<STROKE>;<CAPITAL>;IGNORE
+<o//> <O>;<STROKE>;<SMALL>;IGNORE
+<O//'> <O>;<STROKE+ACUTE>;<CAPITAL>;IGNORE
+<o//'> <O>;<STROKE+ACUTE>;<SMALL>;IGNORE
+<O;> <O>;<OGONEK>;<CAPITAL>;IGNORE
+<o;> <O>;<OGONEK>;<SMALL>;IGNORE
+<O1> <O>;<OGONEK+MACRON>;<CAPITAL>;IGNORE
+<o1> <O>;<OGONEK+MACRON>;<SMALL>;IGNORE
+<O-> <O>;<MACRON>;<CAPITAL>;IGNORE
+<o-> <O>;<MACRON>;<SMALL>;IGNORE
+<O-'> <O>;<MACRON+ACUTE>;<CAPITAL>;IGNORE
+<o-'> <O>;<MACRON+ACUTE>;<SMALL>;IGNORE
+<O-!> <O>;<MACRON+GRAVE>;<CAPITAL>;IGNORE
+<o-!> <O>;<MACRON+GRAVE>;<SMALL>;IGNORE
+<O9> <O>;<HORN>;<CAPITAL>;IGNORE
+<o9> <O>;<HORN>;<SMALL>;IGNORE
+<O9'> <O>;<HORN+ACUTE>;<CAPITAL>;IGNORE
+<o9'> <O>;<HORN+ACUTE>;<SMALL>;IGNORE
+<O9!> <O>;<HORN+GRAVE>;<CAPITAL>;IGNORE
+<o9!> <O>;<HORN+GRAVE>;<SMALL>;IGNORE
+<O92> <O>;<HORN+HOOK>;<CAPITAL>;IGNORE
+<o92> <O>;<HORN+HOOK>;<SMALL>;IGNORE
+<O9?> <O>;<HORN+TILDE>;<CAPITAL>;IGNORE
+<o9?> <O>;<HORN+TILDE>;<SMALL>;IGNORE
+<O9-.> <O>;<HORN+DOT-BELOW>;<CAPITAL>;IGNORE
+<o9-.> <O>;<HORN+DOT-BELOW>;<SMALL>;IGNORE
+<o8>
+<OE> "<O><E>";"<OE><OE>";"<CAPITAL><CAPITAL>";IGNORE
+<oe> "<O><E>";"<OE><OE>";"<SMALL><SMALL>";IGNORE
+<P> <P>;<NONE>;<CAPITAL>;IGNORE
+<p> <P>;<NONE>;<SMALL>;IGNORE
+<P'> <P>;<ACUTE>;<CAPITAL>;IGNORE
+<p'> <P>;<ACUTE>;<SMALL>;IGNORE
+<P.> <P>;<DOT>;<CAPITAL>;IGNORE
+<p.> <P>;<DOT>;<SMALL>;IGNORE
+<p8>
+<Q> <Q>;<NONE>;<CAPITAL>;IGNORE
+<q> <Q>;<NONE>;<SMALL>;IGNORE
+<q8>
+<R> <R>;<NONE>;<CAPITAL>;IGNORE
+<r> <R>;<NONE>;<SMALL>;IGNORE
+<R'> <R>;<ACUTE>;<CAPITAL>;IGNORE
+<r'> <R>;<ACUTE>;<SMALL>;IGNORE
+<R!!> <R>;<DOUBLE-GRAVE>;<CAPITAL>;IGNORE
+<r!!> <R>;<DOUBLE-GRAVE>;<SMALL>;IGNORE
+<R)> <R>;<INVERTED-BREVE>;<CAPITAL>;IGNORE
+<r)> <R>;<INVERTED-BREVE>;<SMALL>;IGNORE
+<R<> <R>;<CARON>;<CAPITAL>;IGNORE
+<r<> <R>;<CARON>;<SMALL>;IGNORE
+<R.> <R>;<DOT>;<CAPITAL>;IGNORE
+<r.> <R>;<DOT>;<SMALL>;IGNORE
+<R-.> <R>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<r-.> <R>;<DOT-BELOW>;<SMALL>;IGNORE
+<R,> <R>;<CEDILLA>;<CAPITAL>;IGNORE
+<r,> <R>;<CEDILLA>;<SMALL>;IGNORE
+<R--.> <R>;<MACRON+DOT-BELOW>;<CAPITAL>;IGNORE
+<r--.> <R>;<MACRON+DOT-BELOW>;<SMALL>;IGNORE
+<R_> <R>;<LINE-BELOW>;<CAPITAL>;IGNORE
+<r_> <R>;<LINE-BELOW>;<SMALL>;IGNORE
+<r8>
+<S> <S>;<NONE>;<CAPITAL>;IGNORE
+<s> <S>;<NONE>;<SMALL>;IGNORE
+<st> "<S><T>";"<NONE><NONE>";"<st><st>";IGNORE
+<S'> <S>;<ACUTE>;<CAPITAL>;IGNORE
+<s'> <S>;<ACUTE>;<SMALL>;IGNORE
+<S'.> <S>;<ACUTE+DOT>;<CAPITAL>;IGNORE
+<s'.> <S>;<ACUTE+DOT>;<SMALL>;IGNORE
+<S/>> <S>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<s/>> <S>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<S<> <S>;<CARON>;<CAPITAL>;IGNORE
+<s<> <S>;<CARON>;<SMALL>;IGNORE
+<S<.> <S>;<CARON+DOT>;<CAPITAL>;IGNORE
+<s<.> <S>;<CARON+DOT>;<SMALL>;IGNORE
+<S.> <S>;<DOT>;<CAPITAL>;IGNORE
+<s.> <S>;<DOT>;<SMALL>;IGNORE
+<S-.> <S>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<s-.> <S>;<DOT-BELOW>;<SMALL>;IGNORE
+<S.-.> <S>;<DOT+DOT-BELOW>;<CAPITAL>;IGNORE
+<s.-.> <S>;<DOT+DOT-BELOW>;<SMALL>;IGNORE
+<S,> <S>;<CEDILLA>;<CAPITAL>;IGNORE
+<s,> <S>;<CEDILLA>;<SMALL>;IGNORE
+<s8>
+<s1> <S>;<s1>;<SMALL>;IGNORE
+<ss> "<S><S>";"<NONE><NONE>";"<SMALL><ss>";IGNORE
+<T> <T>;<NONE>;<CAPITAL>;IGNORE
+<t> <T>;<NONE>;<SMALL>;IGNORE
+<T<> <T>;<CARON>;<CAPITAL>;IGNORE
+<t<> <T>;<CARON>;<SMALL>;IGNORE
+<T.> <T>;<DOT>;<CAPITAL>;IGNORE
+<t.> <T>;<DOT>;<SMALL>;IGNORE
+<T-.> <T>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<t-.> <T>;<DOT-BELOW>;<SMALL>;IGNORE
+<T//> <T>;<STROKE>;<CAPITAL>;IGNORE
+<t//> <T>;<STROKE>;<SMALL>;IGNORE
+<T,> <T>;<CEDILLA>;<CAPITAL>;IGNORE
+<t,> <T>;<CEDILLA>;<SMALL>;IGNORE
+<T-/>> <T>;<MACRON+CIRCUMFLEX>;<CAPITAL>;IGNORE
+<t-/>> <T>;<MACRON+CIRCUMFLEX>;<SMALL>;IGNORE
+<T_> <T>;<LINE-BELOW>;<CAPITAL>;IGNORE
+<t_> <T>;<LINE-BELOW>;<SMALL>;IGNORE
+<t8>
+<TH> "<T><H>";"<TH><TH>";"<CAPITAL><CAPITAL>";IGNORE
+<th> "<T><H>";"<TH><TH>";"<SMALL><SMALL>";IGNORE
+<U> <U>;<NONE>;<CAPITAL>;IGNORE
+<u> <U>;<NONE>;<SMALL>;IGNORE
+<U'> <U>;<ACUTE>;<CAPITAL>;IGNORE
+<u'> <U>;<ACUTE>;<SMALL>;IGNORE
+<U!> <U>;<GRAVE>;<CAPITAL>;IGNORE
+<u!> <U>;<GRAVE>;<SMALL>;IGNORE
+<U!!> <U>;<DOUBLE-GRAVE>;<CAPITAL>;IGNORE
+<u!!> <U>;<DOUBLE-GRAVE>;<SMALL>;IGNORE
+<U(> <U>;<BREVE>;<CAPITAL>;IGNORE
+<u(> <U>;<BREVE>;<SMALL>;IGNORE
+<U)> <U>;<INVERTED-BREVE>;<CAPITAL>;IGNORE
+<u)> <U>;<INVERTED-BREVE>;<SMALL>;IGNORE
+<U/>> <U>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<u/>> <U>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<U<> <U>;<CARON>;<CAPITAL>;IGNORE
+<u<> <U>;<CARON>;<SMALL>;IGNORE
+<U0> <U>;<RING>;<CAPITAL>;IGNORE
+<u0> <U>;<RING>;<SMALL>;IGNORE
+<U:> <U>;<DIAERESIS>;<CAPITAL>;IGNORE
+<u:> <U>;<DIAERESIS>;<SMALL>;IGNORE
+<U:-> <U>;<DIAERESIS+MACRON>;<CAPITAL>;IGNORE
+<u:-> <U>;<DIAERESIS+MACRON>;<SMALL>;IGNORE
+<U:'> <U>;<DIAERESIS+ACUTE>;<CAPITAL>;IGNORE
+<u:'> <U>;<DIAERESIS+ACUTE>;<SMALL>;IGNORE
+<U:!> <U>;<DIAERESIS+GRAVE>;<CAPITAL>;IGNORE
+<u:!> <U>;<DIAERESIS+GRAVE>;<SMALL>;IGNORE
+<U:<> <U>;<DIAERESIS+CARON>;<CAPITAL>;IGNORE
+<u:<> <U>;<DIAERESIS+CARON>;<SMALL>;IGNORE
+<U"> <U>;<DOUBLE-ACUTE>;<CAPITAL>;IGNORE
+<u"> <U>;<DOUBLE-ACUTE>;<SMALL>;IGNORE
+<U2> <U>;<HOOK>;<CAPITAL>;IGNORE
+<u2> <U>;<HOOK>;<SMALL>;IGNORE
+<U?> <U>;<TILDE>;<CAPITAL>;IGNORE
+<u?> <U>;<TILDE>;<SMALL>;IGNORE
+<U?'> <U>;<TILDE+ACUTE>;<CAPITAL>;IGNORE
+<u?'> <U>;<TILDE+ACUTE>;<SMALL>;IGNORE
+<U-?> <U>;<TILDE-BELOW>;<CAPITAL>;IGNORE
+<u-?> <U>;<TILDE-BELOW>;<SMALL>;IGNORE
+<U-.> <U>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<u-.> <U>;<DOT-BELOW>;<SMALL>;IGNORE
+<U;> <U>;<OGONEK>;<CAPITAL>;IGNORE
+<u;> <U>;<OGONEK>;<SMALL>;IGNORE
+<U-> <U>;<MACRON>;<CAPITAL>;IGNORE
+<u-> <U>;<MACRON>;<SMALL>;IGNORE
+<U-:> <U>;<MACRON+DIAERESIS>;<CAPITAL>;IGNORE
+<u-:> <U>;<MACRON+DIAERESIS>;<SMALL>;IGNORE
+<U--:> <U>;<MACRON+DIAERESIS-BELOW>;<CAPITAL>;IGNORE
+<u--:> <U>;<MACRON+DIAERESIS-BELOW>;<SMALL>;IGNORE
+<U-/>> <U>;<MACRON+CIRCUMFLEX>;<CAPITAL>;IGNORE
+<u-/>> <U>;<MACRON+CIRCUMFLEX>;<SMALL>;IGNORE
+<U9> <U>;<HORN>;<CAPITAL>;IGNORE
+<u9> <U>;<HORN>;<SMALL>;IGNORE
+<U9'> <U>;<HORN+ACUTE>;<CAPITAL>;IGNORE
+<u9'> <U>;<HORN+ACUTE>;<SMALL>;IGNORE
+<U9!> <U>;<HORN+GRAVE>;<CAPITAL>;IGNORE
+<u9!> <U>;<HORN+GRAVE>;<SMALL>;IGNORE
+<U92> <U>;<HORN+HOOK>;<CAPITAL>;IGNORE
+<u92> <U>;<HORN+HOOK>;<SMALL>;IGNORE
+<U9?> <U>;<HORN+TILDE>;<CAPITAL>;IGNORE
+<u9?> <U>;<HORN+TILDE>;<SMALL>;IGNORE
+<U9-.> <U>;<HORN+DOT-BELOW>;<CAPITAL>;IGNORE
+<u9-.> <U>;<HORN+DOT-BELOW>;<SMALL>;IGNORE
+<u8>
+<V> <V>;<NONE>;<CAPITAL>;IGNORE
+<v> <V>;<NONE>;<SMALL>;IGNORE
+<V?> <V>;<TILDE>;<CAPITAL>;IGNORE
+<v?> <V>;<TILDE>;<SMALL>;IGNORE
+<V-.> <V>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<v-.> <V>;<DOT-BELOW>;<SMALL>;IGNORE
+<v8>
+<W> <W>;<NONE>;<CAPITAL>;IGNORE
+<w> <W>;<NONE>;<SMALL>;IGNORE
+<W'> <W>;<ACUTE>;<CAPITAL>;IGNORE
+<w'> <W>;<ACUTE>;<SMALL>;IGNORE
+<W!> <W>;<GRAVE>;<CAPITAL>;IGNORE
+<w!> <W>;<GRAVE>;<SMALL>;IGNORE
+<W/>> <W>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<w/>> <W>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<W:> <W>;<DIAERESIS>;<CAPITAL>;IGNORE
+<w:> <W>;<DIAERESIS>;<SMALL>;IGNORE
+<W.> <W>;<DOT>;<CAPITAL>;IGNORE
+<w.> <W>;<DOT>;<SMALL>;IGNORE
+<W-.> <W>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<w-.> <W>;<DOT-BELOW>;<SMALL>;IGNORE
+<w8>
+<X> <X>;<NONE>;<CAPITAL>;IGNORE
+<x> <X>;<NONE>;<SMALL>;IGNORE
+<X:> <X>;<DIAERESIS>;<CAPITAL>;IGNORE
+<x:> <X>;<DIAERESIS>;<SMALL>;IGNORE
+<X.> <X>;<DOT>;<CAPITAL>;IGNORE
+<x.> <X>;<DOT>;<SMALL>;IGNORE
+<x8>
+<Y> <Y>;<NONE>;<CAPITAL>;IGNORE
+<y> <Y>;<NONE>;<SMALL>;IGNORE
+<Y'> <Y>;<ACUTE>;<CAPITAL>;IGNORE
+<y'> <Y>;<ACUTE>;<SMALL>;IGNORE
+<Y!> <Y>;<GRAVE>;<CAPITAL>;IGNORE
+<y!> <Y>;<GRAVE>;<SMALL>;IGNORE
+<Y/>> <Y>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<y/>> <Y>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<Y:> <Y>;<DIAERESIS>;<CAPITAL>;IGNORE
+<y:> <Y>;<DIAERESIS>;<SMALL>;IGNORE
+<Y2> <Y>;<HOOK>;<CAPITAL>;IGNORE
+<y2> <Y>;<HOOK>;<SMALL>;IGNORE
+<Y?> <Y>;<TILDE>;<CAPITAL>;IGNORE
+<y?> <Y>;<TILDE>;<SMALL>;IGNORE
+<Y.> <Y>;<DOT>;<CAPITAL>;IGNORE
+<y.> <Y>;<DOT>;<SMALL>;IGNORE
+<Y-.> <Y>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<y-.> <Y>;<DOT-BELOW>;<SMALL>;IGNORE
+<y8>
+<Z> <Z>;<NONE>;<CAPITAL>;IGNORE
+<z> <Z>;<NONE>;<SMALL>;IGNORE
+<Z'> <Z>;<ACUTE>;<CAPITAL>;IGNORE
+<z'> <Z>;<ACUTE>;<SMALL>;IGNORE
+<Z/>> <Z>;<CIRCUMFLEX>;<CAPITAL>;IGNORE
+<z/>> <Z>;<CIRCUMFLEX>;<SMALL>;IGNORE
+<Z<> <Z>;<CARON>;<CAPITAL>;IGNORE
+<z<> <Z>;<CARON>;<SMALL>;IGNORE
+<Z.> <Z>;<DOT>;<CAPITAL>;IGNORE
+<z.> <Z>;<DOT>;<SMALL>;IGNORE
+<Z-.> <Z>;<DOT-BELOW>;<CAPITAL>;IGNORE
+<z-.> <Z>;<DOT-BELOW>;<SMALL>;IGNORE
+<Z//> <Z>;<STROKE>;<CAPITAL>;IGNORE
+<z//> <Z>;<STROKE>;<SMALL>;IGNORE
+<Z_> <Z>;<LINE-BELOW>;<CAPITAL>;IGNORE
+<z_> <Z>;<LINE-BELOW>;<SMALL>;IGNORE
+<z8>
+<A*> <A*>;<CAPITAL>;<GREEK>;IGNORE
+<A%> <A*>;<CAPITAL>;<TONOS>;IGNORE
+<a*> <A*>;<SMALL>;<GREEK>;IGNORE
+<a%> <A*>;<SMALL>;<TONOS>;IGNORE
+<B*> <B*>;<CAPITAL>;<GREEK>;IGNORE
+<b*> <B*>;<SMALL>;<GREEK>;IGNORE
+<G*> <G*>;<CAPITAL>;<GREEK>;IGNORE
+<g*> <G*>;<SMALL>;<GREEK>;IGNORE
+<D*> <D*>;<CAPITAL>;<GREEK>;IGNORE
+<d*> <D*>;<SMALL>;<GREEK>;IGNORE
+<E*> <E*>;<CAPITAL>;<GREEK>;IGNORE
+<E%> <E*>;<CAPITAL>;<TONOS>;IGNORE
+<e*> <E*>;<SMALL>;<GREEK>;IGNORE
+<e%> <E*>;<SMALL>;<TONOS>;IGNORE
+<Z*> <Z*>;<CAPITAL>;<GREEK>;IGNORE
+<z*> <Z*>;<SMALL>;<GREEK>;IGNORE
+<Y*> <Y*>;<CAPITAL>;<GREEK>;IGNORE
+<Y%> <Y*>;<CAPITAL>;<TONOS>;IGNORE
+<y*> <Y*>;<SMALL>;<GREEK>;IGNORE
+<y%> <Y*>;<SMALL>;<TONOS>;IGNORE
+<H*> <H*>;<CAPITAL>;<GREEK>;IGNORE
+<h*> <H*>;<SMALL>;<GREEK>;IGNORE
+<I*> <I*>;<CAPITAL>;<GREEK>;IGNORE
+<I%> <I*>;<CAPITAL>;<TONOS>;IGNORE
+<J*> <I*>;<CAPITAL>;<DIALYTICA>;IGNORE
+<i*> <I*>;<SMALL>;<GREEK>;IGNORE
+<i%> <I*>;<SMALL>;<TONOS>;IGNORE
+<j*> <I*>;<SMALL>;<DIALYTICA>;IGNORE
+<i3> <I*>;<SMALL>;<DIALYTICA+TONOS>;IGNORE
+<K*> <K*>;<CAPITAL>;<GREEK>;IGNORE
+<k*> <K*>;<SMALL>;<GREEK>;IGNORE
+<L*> <L*>;<CAPITAL>;<GREEK>;IGNORE
+<l*> <L*>;<SMALL>;<GREEK>;IGNORE
+<M*> <M*>;<CAPITAL>;<GREEK>;IGNORE
+<m*> <M*>;<SMALL>;<GREEK>;IGNORE
+<N*> <N*>;<CAPITAL>;<GREEK>;IGNORE
+<n*> <N*>;<SMALL>;<GREEK>;IGNORE
+<C*> <C*>;<CAPITAL>;<GREEK>;IGNORE
+<c*> <C*>;<SMALL>;<GREEK>;IGNORE
+<O*> <O*>;<CAPITAL>;<GREEK>;IGNORE
+<O%> <O*>;<CAPITAL>;<TONOS>;IGNORE
+<o*> <O*>;<SMALL>;<GREEK>;IGNORE
+<o%> <O*>;<SMALL>;<TONOS>;IGNORE
+<P*> <P*>;<CAPITAL>;<GREEK>;IGNORE
+<p*> <P*>;<SMALL>;<GREEK>;IGNORE
+<R*> <R*>;<CAPITAL>;<GREEK>;IGNORE
+<r*> <R*>;<SMALL>;<GREEK>;IGNORE
+<S*> <S*>;<CAPITAL>;<GREEK>;IGNORE
+<s*> <S*>;<SMALL>;<GREEK>;IGNORE
+<*s> <S*>;<SMALL>;<*s>;IGNORE
+<T*> <T*>;<CAPITAL>;<GREEK>;IGNORE
+<t*> <T*>;<SMALL>;<GREEK>;IGNORE
+<U*> <U*>;<CAPITAL>;<GREEK>;IGNORE
+<U%> <U*>;<CAPITAL>;<TONOS>;IGNORE
+<V*> <U*>;<CAPITAL>;<DIALYTICA>;IGNORE
+<u*> <U*>;<SMALL>;<GREEK>;IGNORE
+<u%> <U*>;<SMALL>;<TONOS>;IGNORE
+<v*> <U*>;<SMALL>;<DIALYTICA>;IGNORE
+<u3> <U*>;<SMALL>;<DIALYTICA+TONOS>;IGNORE
+<F*> <F*>;<CAPITAL>;<GREEK>;IGNORE
+<f*> <F*>;<SMALL>;<GREEK>;IGNORE
+<X*> <X*>;<CAPITAL>;<GREEK>;IGNORE
+<x*> <X*>;<SMALL>;<GREEK>;IGNORE
+<Q*> <Q*>;<CAPITAL>;<GREEK>;IGNORE
+<q*> <Q*>;<SMALL>;<GREEK>;IGNORE
+<W*> <W*>;<CAPITAL>;<GREEK>;IGNORE
+<W%> <W*>;<CAPITAL>;<TONOS>;IGNORE
+<w*> <W*>;<SMALL>;<GREEK>;IGNORE
+<w%> <W*>;<SMALL>;<TONOS>;IGNORE
+<A=> <A=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<a=> <A=>;<CYRILLIC>;<SMALL>;IGNORE
+<B=> <B=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<b=> <B=>;<CYRILLIC>;<SMALL>;IGNORE
+<V=> <V=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<v=> <V=>;<CYRILLIC>;<SMALL>;IGNORE
+<G=> <G=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<g=> <G=>;<CYRILLIC>;<SMALL>;IGNORE
+<G%> <G%>;<CYRILLIC>;<CAPITAL>;IGNORE
+<g%> <G%>;<CYRILLIC>;<SMALL>;IGNORE
+<G3> <G3>;<CYRILLIC>;<CAPITAL>;IGNORE
+<g3> <G3>;<CYRILLIC>;<SMALL>;IGNORE
+<D=> <D=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<d=> <D=>;<CYRILLIC>;<SMALL>;IGNORE
+<D%> <D%>;<CYRILLIC>;<CAPITAL>;IGNORE
+<d%> <D%>;<CYRILLIC>;<SMALL>;IGNORE
+<E=> <E=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<e=> <E=>;<CYRILLIC>;<SMALL>;IGNORE
+<IO> <IO>;<CYRILLIC>;<CAPITAL>;IGNORE
+<io> <IO>;<CYRILLIC>;<SMALL>;IGNORE
+<IE> <IE>;<CYRILLIC>;<CAPITAL>;IGNORE
+<ie> <IE>;<CYRILLIC>;<SMALL>;IGNORE
+<Z%> <Z%>;<CYRILLIC>;<CAPITAL>;IGNORE
+<z%> <Z%>;<CYRILLIC>;<SMALL>;IGNORE
+<Z=> <Z=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<z=> <Z=>;<CYRILLIC>;<SMALL>;IGNORE
+<I=> <I=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<i=> <I=>;<CYRILLIC>;<SMALL>;IGNORE
+<II> <II>;<CYRILLIC>;<CAPITAL>;IGNORE
+<ii> <II>;<CYRILLIC>;<SMALL>;IGNORE
+<YI> <YI>;<CYRILLIC>;<CAPITAL>;IGNORE
+<yi> <YI>;<CYRILLIC>;<SMALL>;IGNORE
+<J%> <J%>;<CYRILLIC>;<CAPITAL>;IGNORE
+<j%> <J%>;<CYRILLIC>;<SMALL>;IGNORE
+<J=> <J=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<j=> <J=>;<CYRILLIC>;<SMALL>;IGNORE
+<K=> <K=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<k=> <K=>;<CYRILLIC>;<SMALL>;IGNORE
+<KJ> <KJ>;<CYRILLIC>;<CAPITAL>;IGNORE
+<kj> <KJ>;<CYRILLIC>;<SMALL>;IGNORE
+<L=> <L=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<l=> <L=>;<CYRILLIC>;<SMALL>;IGNORE
+<LJ> <LJ>;<CYRILLIC>;<CAPITAL>;IGNORE
+<lj> <LJ>;<CYRILLIC>;<SMALL>;IGNORE
+<M=> <M=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<m=> <M=>;<CYRILLIC>;<SMALL>;IGNORE
+<N=> <N=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<n=> <N=>;<CYRILLIC>;<SMALL>;IGNORE
+<NJ> <NJ>;<CYRILLIC>;<CAPITAL>;IGNORE
+<nj> <NJ>;<CYRILLIC>;<SMALL>;IGNORE
+<O=> <O=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<o=> <O=>;<CYRILLIC>;<SMALL>;IGNORE
+<P=> <P=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<p=> <P=>;<CYRILLIC>;<SMALL>;IGNORE
+<R=> <R=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<r=> <R=>;<CYRILLIC>;<SMALL>;IGNORE
+<S=> <S=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<s=> <S=>;<CYRILLIC>;<SMALL>;IGNORE
+<T=> <T=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<t=> <T=>;<CYRILLIC>;<SMALL>;IGNORE
+<Ts> <Ts>;<CYRILLIC>;<CAPITAL>;IGNORE
+<ts> <Ts>;<CYRILLIC>;<SMALL>;IGNORE
+<U=> <U=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<u=> <U=>;<CYRILLIC>;<SMALL>;IGNORE
+<V%> <V%>;<CYRILLIC>;<CAPITAL>;IGNORE
+<v%> <V%>;<CYRILLIC>;<SMALL>;IGNORE
+<F=> <F=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<f=> <F=>;<CYRILLIC>;<SMALL>;IGNORE
+<H=> <H=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<h=> <H=>;<CYRILLIC>;<SMALL>;IGNORE
+<C=> <C=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<c=> <C=>;<CYRILLIC>;<SMALL>;IGNORE
+<DS> <DS>;<CYRILLIC>;<CAPITAL>;IGNORE
+<ds> <DS>;<CYRILLIC>;<SMALL>;IGNORE
+<C%> <C%>;<CYRILLIC>;<CAPITAL>;IGNORE
+<c%> <C%>;<CYRILLIC>;<SMALL>;IGNORE
+<DZ> <DZ>;<CYRILLIC>;<CAPITAL>;IGNORE
+<dz> <DZ>;<CYRILLIC>;<SMALL>;IGNORE
+<S%> <S%>;<CYRILLIC>;<CAPITAL>;IGNORE
+<s%> <S%>;<CYRILLIC>;<SMALL>;IGNORE
+<Sc> <Sc>;<CYRILLIC>;<CAPITAL>;IGNORE
+<sc> <Sc>;<CYRILLIC>;<SMALL>;IGNORE
+<='> <='>;<CYRILLIC>;<SMALL>;IGNORE
+<="> <='>;<CYRILLIC>;<CAPITAL>;IGNORE
+<Y=> <Y=>;<CYRILLIC>;<CAPITAL>;IGNORE
+<y=> <Y=>;<CYRILLIC>;<SMALL>;IGNORE
+<%'> <%'>;<CYRILLIC>;<SMALL>;IGNORE
+<%"> <%'>;<CYRILLIC>;<CAPITAL>;IGNORE
+<JE> <JE>;<CYRILLIC>;<CAPITAL>;IGNORE
+<je> <JE>;<CYRILLIC>;<SMALL>;IGNORE
+<JU> <JU>;<CYRILLIC>;<CAPITAL>;IGNORE
+<ju> <JU>;<CYRILLIC>;<SMALL>;IGNORE
+<JA> <JA>;<CYRILLIC>;<CAPITAL>;IGNORE
+<ja> <JA>;<CYRILLIC>;<SMALL>;IGNORE
+<Y3> <Y3>;<CYRILLIC>;<CAPITAL>;IGNORE
+<y3> <Y3>;<CYRILLIC>;<SMALL>;IGNORE
+<O3> <O3>;<CYRILLIC>;<CAPITAL>;IGNORE
+<o3> <O3>;<CYRILLIC>;<SMALL>;IGNORE
+<F3> <F3>;<CYRILLIC>;<CAPITAL>;IGNORE
+<f3> <F3>;<CYRILLIC>;<SMALL>;IGNORE
+<V3> <V3>;<CYRILLIC>;<CAPITAL>;IGNORE
+<v3> <V3>;<CYRILLIC>;<SMALL>;IGNORE
+<C3> <C3>;<CYRILLIC>;<CAPITAL>;IGNORE
+<c3> <C3>;<CYRILLIC>;<SMALL>;IGNORE
+<A+> <A+>;IGNORE;IGNORE;IGNORE
+<B+> <B+>;IGNORE;IGNORE;IGNORE
+<G+> <G+>;IGNORE;IGNORE;IGNORE
+<D+> <D+>;IGNORE;IGNORE;IGNORE
+<H+> <H+>;IGNORE;IGNORE;IGNORE
+<W+> <W+>;IGNORE;IGNORE;IGNORE
+<Z+> <Z+>;IGNORE;IGNORE;IGNORE
+<X+> <X+>;IGNORE;IGNORE;IGNORE
+<Tj> <Tj>;IGNORE;IGNORE;IGNORE
+<J+> <J+>;IGNORE;IGNORE;IGNORE
+<K%> <K%>;IGNORE;IGNORE;IGNORE
+<K+> <K+>;IGNORE;IGNORE;IGNORE
+<L+> <L+>;IGNORE;IGNORE;IGNORE
+<M%> <M%>;IGNORE;IGNORE;IGNORE
+<M+> <M+>;IGNORE;IGNORE;IGNORE
+<N%> <N%>;IGNORE;IGNORE;IGNORE
+<N+> <N+>;IGNORE;IGNORE;IGNORE
+<S+> <S+>;IGNORE;IGNORE;IGNORE
+<E+> <E+>;IGNORE;IGNORE;IGNORE
+<P%> <P%>;IGNORE;IGNORE;IGNORE
+<P+> <P+>;IGNORE;IGNORE;IGNORE
+<Zj> <Zj>;IGNORE;IGNORE;IGNORE
+<ZJ> <ZJ>;IGNORE;IGNORE;IGNORE
+<Q+> <Q+>;IGNORE;IGNORE;IGNORE
+<R+> <R+>;IGNORE;IGNORE;IGNORE
+<Sh> <Sh>;IGNORE;IGNORE;IGNORE
+<T+> <T+>;IGNORE;IGNORE;IGNORE
+
+% Arabic collating
+
+<,+> IGNORE;IGNORE;IGNORE;<,+>
+<;+> IGNORE;IGNORE;IGNORE;<;+>
+<?+> IGNORE;IGNORE;IGNORE;<?+>
+<++> IGNORE;IGNORE;IGNORE;<++>
+
+<H'> <H'>;<H'>;IGNORE;IGNORE
+<aM> <aM>;<aM>;IGNORE;IGNORE
+<aM.> <aM>;<aM.>;IGNORE;IGNORE
+<aH> <H'>;<aH>;IGNORE;IGNORE
+<aH.> <H'>;<aH.>;IGNORE;IGNORE
+<wH> <H'>;<wH>;IGNORE;IGNORE
+<ah> <H'>;<ah>;IGNORE;IGNORE
+<ah.> <H'>;<ah.>;IGNORE;IGNORE
+<yH> <H'>;<yH>;IGNORE;IGNORE
+<aS> <aS>;<aS>;IGNORE;IGNORE
+<a+> <a+>;<a+>;IGNORE;IGNORE
+<a+-> <a+>;<a+->;IGNORE;IGNORE
+<a+.> <a+>;<a+.>;IGNORE;IGNORE
+<a+:> <a+>;<a+:>;IGNORE;IGNORE
+<b+> <b+>;<b+>;IGNORE;IGNORE
+<b+-> <b+>;<b+->;IGNORE;IGNORE
+<b+.> <b+>;<b+.>;IGNORE;IGNORE
+<b+,> <b+>;<b+,>;IGNORE;IGNORE
+<b+;> <b+>;<b+;>;IGNORE;IGNORE
+<p+> <p+>;<p+>;IGNORE;IGNORE
+<v+> <v+>;<v+>;IGNORE;IGNORE
+<tm> <tm>;<tm>;IGNORE;IGNORE
+<tm-> <tm>;<tm->;IGNORE;IGNORE
+<tm.> <tm>;<tm.>;IGNORE;IGNORE
+<t+> <tm>;<t+>;IGNORE;IGNORE
+<t+-> <tm>;<t+->;IGNORE;IGNORE
+<t+.> <tm>;<t+.>;IGNORE;IGNORE
+<t+,> <tm>;<t+,>;IGNORE;IGNORE
+<t+;> <tm>;<t+;>;IGNORE;IGNORE
+<tk> <tk>;<tk>;IGNORE;IGNORE
+<tk-> <tk>;<tk->;IGNORE;IGNORE
+<tk.> <tk>;<tk.>;IGNORE;IGNORE
+<tk,> <tk>;<tk,>;IGNORE;IGNORE
+<tk;> <tk>;<tk;>;IGNORE;IGNORE
+<g+> <g+>;<g+>;IGNORE;IGNORE
+<g+-> <g+>;<g+->;IGNORE;IGNORE
+<g+.> <g+>;<g+.>;IGNORE;IGNORE
+<g+,> <g+>;<g+,>;IGNORE;IGNORE
+<g+;> <g+>;<g+;>;IGNORE;IGNORE
+<hk> <hk>;<hk>;IGNORE;IGNORE
+<hk-> <hk>;<hk->;IGNORE;IGNORE
+<hk.> <hk>;<hk.>;IGNORE;IGNORE
+<hk,> <hk>;<hk,>;IGNORE;IGNORE
+<hk;> <hk>;<hk;>;IGNORE;IGNORE
+<x+> <x+>;<x+>;IGNORE;IGNORE
+<x+-> <x+>;<x+->;IGNORE;IGNORE
+<x+.> <x+>;<x+.>;IGNORE;IGNORE
+<x+,> <x+>;<x+,>;IGNORE;IGNORE
+<x+;> <x+>;<x+;>;IGNORE;IGNORE
+<d+> <d+>;<d+>;IGNORE;IGNORE
+<d+-> <d+>;<d+->;IGNORE;IGNORE
+<d+.> <d+>;<d+.>;IGNORE;IGNORE
+<dk> <dk>;<dk>;IGNORE;IGNORE
+<dk-> <dk>;<dk->;IGNORE;IGNORE
+<dk.> <dk>;<dk.>;IGNORE;IGNORE
+<r+> <r+>;<r+>;IGNORE;IGNORE
+<r+-> <r+>;<r+->;IGNORE;IGNORE
+<r+.> <r+>;<r+.>;IGNORE;IGNORE
+<z+> <z+>;<z+>;IGNORE;IGNORE
+<z+-> <z+>;<z+->;IGNORE;IGNORE
+<z+.> <z+>;<z+.>;IGNORE;IGNORE
+<s+> <s+>;<s+>;IGNORE;IGNORE
+<s+-> <s+>;<s+->;IGNORE;IGNORE
+<s+.> <s+>;<s+.>;IGNORE;IGNORE
+<s+,> <s+>;<s+,>;IGNORE;IGNORE
+<s+;> <s+>;<s+;>;IGNORE;IGNORE
+<sn> <sn>;<sn>;IGNORE;IGNORE
+<sn-> <sn>;<sn->;IGNORE;IGNORE
+<sn.> <sn>;<sn.>;IGNORE;IGNORE
+<sn,> <sn>;<sn,>;IGNORE;IGNORE
+<sn;> <sn>;<sn;>;IGNORE;IGNORE
+<c+> <c+>;<c+>;IGNORE;IGNORE
+<c+-> <c+>;<c+->;IGNORE;IGNORE
+<c+.> <c+>;<c+.>;IGNORE;IGNORE
+<c+,> <c+>;<c+,>;IGNORE;IGNORE
+<c+;> <c+>;<c+;>;IGNORE;IGNORE
+<dd> <dd>;<dd>;IGNORE;IGNORE
+<dd-> <dd>;<dd->;IGNORE;IGNORE
+<dd.> <dd>;<dd.>;IGNORE;IGNORE
+<dd,> <dd>;<dd,>;IGNORE;IGNORE
+<dd;> <dd>;<dd;>;IGNORE;IGNORE
+<tj> <tj>;<tj>;IGNORE;IGNORE
+<tj-> <tj>;<tj->;IGNORE;IGNORE
+<tj.> <tj>;<tj.>;IGNORE;IGNORE
+<tj,> <tj>;<tj,>;IGNORE;IGNORE
+<tj;> <tj>;<tj;>;IGNORE;IGNORE
+<zH> <zH>;<zH>;IGNORE;IGNORE
+<zH-> <zH>;<zH->;IGNORE;IGNORE
+<zH.> <zH>;<zH.>;IGNORE;IGNORE
+<zH,> <zH>;<zH,>;IGNORE;IGNORE
+<zH;> <zH>;<zH;>;IGNORE;IGNORE
+<e+> <e+>;<e+>;IGNORE;IGNORE
+<e+-> <e+>;<e+->;IGNORE;IGNORE
+<e+.> <e+>;<e+.>;IGNORE;IGNORE
+<e+,> <e+>;<e+,>;IGNORE;IGNORE
+<e+;> <e+>;<e+;>;IGNORE;IGNORE
+<i+> <i+>;<i+>;IGNORE;IGNORE
+<i+-> <i+>;<i+->;IGNORE;IGNORE
+<i+.> <i+>;<i+.>;IGNORE;IGNORE
+<i+,> <i+>;<i+,>;IGNORE;IGNORE
+<i+;> <i+>;<i+;>;IGNORE;IGNORE
+<f+> <f+>;<f+>;IGNORE;IGNORE
+<f+-> <f+>;<f+->;IGNORE;IGNORE
+<f+.> <f+>;<f+.>;IGNORE;IGNORE
+<f+,> <f+>;<f+,>;IGNORE;IGNORE
+<f+;> <f+>;<f+;>;IGNORE;IGNORE
+<q+> <q+>;<q+>;IGNORE;IGNORE
+<q+-> <q+>;<q+->;IGNORE;IGNORE
+<q+.> <q+>;<q+.>;IGNORE;IGNORE
+<q+,> <q+>;<q+,>;IGNORE;IGNORE
+<q+;> <q+>;<q+;>;IGNORE;IGNORE
+<k+> <k+>;<k+>;IGNORE;IGNORE
+<k+-> <k+>;<k+->;IGNORE;IGNORE
+<k+.> <k+>;<k+.>;IGNORE;IGNORE
+<k+,> <k+>;<k+,>;IGNORE;IGNORE
+<k+;> <k+>;<k+;>;IGNORE;IGNORE
+<l+> <l+>;<l+>;IGNORE;IGNORE
+<l+-> <l+>;<l+->;IGNORE;IGNORE
+<l+.> <l+>;<l+.>;IGNORE;IGNORE
+<l+,> <l+>;<l+,>;IGNORE;IGNORE
+<l+;> <l+>;<l+;>;IGNORE;IGNORE
+<m+> <m+>;<m+>;IGNORE;IGNORE
+<m+-> <m+>;<m+->;IGNORE;IGNORE
+<m+.> <m+>;<m+.>;IGNORE;IGNORE
+<m+,> <m+>;<m+,>;IGNORE;IGNORE
+<m+;> <m+>;<m+;>;IGNORE;IGNORE
+<n+> <n+>;<n+>;IGNORE;IGNORE
+<n+-> <n+>;<n+->;IGNORE;IGNORE
+<n+.> <n+>;<n+.>;IGNORE;IGNORE
+<n+,> <n+>;<n+,>;IGNORE;IGNORE
+<n+;> <n+>;<n+;>;IGNORE;IGNORE
+<h+> <h+>;<h+>;IGNORE;IGNORE
+<h+-> <h+>;<h+->;IGNORE;IGNORE
+<h+.> <h+>;<h+.>;IGNORE;IGNORE
+<h+,> <h+>;<h+,>;IGNORE;IGNORE
+<h+;> <h+>;<h+;>;IGNORE;IGNORE
+<w+> <w+>;<w+>;IGNORE;IGNORE
+<w+-> <w+>;<w+->;IGNORE;IGNORE
+<w+.> <w+>;<w+.>;IGNORE;IGNORE
+<j+> <j+>;<j+>;IGNORE;IGNORE
+<j+-> <j+>;<j+->;IGNORE;IGNORE
+<j+.> <j+>;<j+.>;IGNORE;IGNORE
+<y+> <y+>;<y+>;IGNORE;IGNORE
+<y+-> <y+>;<y+->;IGNORE;IGNORE
+<y+.> <y+>;<y+.>;IGNORE;IGNORE
+<y+,> <y+>;<y+,>;IGNORE;IGNORE
+<y+;> <y+>;<y+;>;IGNORE;IGNORE
+
+<:+> IGNORE;IGNORE;<:+>;IGNORE
+<"+> IGNORE;IGNORE;<"+>;IGNORE
+<=+> IGNORE;IGNORE;<=+>;IGNORE
+<//+> IGNORE;IGNORE;<//+>;IGNORE
+<'+> IGNORE;IGNORE;<'+>;IGNORE
+<1+> IGNORE;IGNORE;<1+>;IGNORE
+<3+> IGNORE;IGNORE;<3+>;IGNORE
+<3+;> IGNORE;IGNORE;<3+;>;IGNORE
+<0+> IGNORE;IGNORE;<0+>;IGNORE
+
+<0a> <0>;<0a>;IGNORE;IGNORE
+<1a> <1>;<1a>;IGNORE;IGNORE
+<2a> <2>;<2a>;IGNORE;IGNORE
+<3a> <3>;<3a>;IGNORE;IGNORE
+<4a> <4>;<4a>;IGNORE;IGNORE
+<5a> <5>;<5a>;IGNORE;IGNORE
+<6a> <6>;<6a>;IGNORE;IGNORE
+<7a> <7>;<7a>;IGNORE;IGNORE
+<8a> <8>;<8a>;IGNORE;IGNORE
+<9a> <9>;<9a>;IGNORE;IGNORE
+
+<lM-> <l+><aM>;<l+><aM>;<lM-><lM->;IGNORE
+<lM.> <l+><aM>;<l+><aM.>;<lM.><lM.>;IGNORE
+<lH-> <l+><aH>;<l+><aH>;<lH-><lH->;IGNORE
+<lH.> <l+><aH>;<l+><aH.>;<lH.><lH.>;IGNORE
+<lh-> <l+><ah>;<l+><ah>;<lh-><lh->;IGNORE
+<lh.> <l+><ah>;<l+><ah.>;<lh.><lh.>;IGNORE
+<la-> <l+><a+>;<l+><a+->;<la-><la->;IGNORE
+<la.> <l+><a+>;<l+><a+.>;<la.><la.>;IGNORE
+
+% katakana/hiragana sorting
+% base is katakana, as this is present in most charsets
+% normal before voiced before semi-voiced
+% small vocals before normal vocals
+% katakana before hiragana
+
+<a6> <a6>;<a6>;IGNORE;IGNORE
+<A5> <a6>;<A5>;IGNORE;IGNORE
+<A6> <a6>;<A6>;IGNORE;IGNORE
+<a5> <a6>;<a5>;IGNORE;IGNORE
+<i6> <i6>;<i6>;IGNORE;IGNORE
+<I5> <i6>;<I5>;IGNORE;IGNORE
+<I6> <i6>;<I6>;IGNORE;IGNORE
+<i5> <i6>;<i5>;IGNORE;IGNORE
+<u6> <u6>;<u6>;IGNORE;IGNORE
+<U5> <u6>;<U5>;IGNORE;IGNORE
+<U6> <u6>;<U6>;IGNORE;IGNORE
+<u5> <u6>;<u5>;IGNORE;IGNORE
+<Vu> <u6>;<Vu>;IGNORE;IGNORE
+<e6> <e6>;<e6>;IGNORE;IGNORE
+<E5> <e6>;<E5>;IGNORE;IGNORE
+<E6> <e6>;<E6>;IGNORE;IGNORE
+<e5> <e6>;<e5>;IGNORE;IGNORE
+<o6> <o6>;<o6>;IGNORE;IGNORE
+<O5> <o6>;<O5>;IGNORE;IGNORE
+<O6> <o6>;<O6>;IGNORE;IGNORE
+<o5> <o6>;<o5>;IGNORE;IGNORE
+<KA> <KA>;<KA>;IGNORE;IGNORE
+<Ka> <KA>;<Ka>;IGNORE;IGNORE
+<ka> <KA>;<ka>;IGNORE;IGNORE
+<Ga> <KA>;<Ga>;IGNORE;IGNORE
+<ga> <KA>;<ga>;IGNORE;IGNORE
+<Ki> <Ki>;<Ki>;IGNORE;IGNORE
+<ki> <Ki>;<ki>;IGNORE;IGNORE
+<Gi> <Ki>;<Gi>;IGNORE;IGNORE
+<gi> <Ki>;<gi>;IGNORE;IGNORE
+<Ku> <Ku>;<Ku>;IGNORE;IGNORE
+<ku> <Ku>;<ku>;IGNORE;IGNORE
+<Gu> <Ku>;<Gu>;IGNORE;IGNORE
+<gu> <Ku>;<gu>;IGNORE;IGNORE
+<KE> <KE>;<KE>;IGNORE;IGNORE
+<Ke> <KE>;<Ke>;IGNORE;IGNORE
+<ke> <KE>;<ke>;IGNORE;IGNORE
+<Ge> <KE>;<Ge>;IGNORE;IGNORE
+<ge> <KE>;<ge>;IGNORE;IGNORE
+<Ko> <Ko>;<Ko>;IGNORE;IGNORE
+<ko> <Ko>;<ko>;IGNORE;IGNORE
+<Go> <Ko>;<Go>;IGNORE;IGNORE
+<go> <Ko>;<go>;IGNORE;IGNORE
+<Sa> <Sa>;<Sa>;IGNORE;IGNORE
+<sa> <Sa>;<sa>;IGNORE;IGNORE
+<Za> <Sa>;<Za>;IGNORE;IGNORE
+<za> <Sa>;<za>;IGNORE;IGNORE
+<Si> <Si>;<Si>;IGNORE;IGNORE
+<si> <Si>;<si>;IGNORE;IGNORE
+<Zi> <Si>;<Zi>;IGNORE;IGNORE
+<zi> <Si>;<zi>;IGNORE;IGNORE
+<Su> <Su>;<Su>;IGNORE;IGNORE
+<su> <Su>;<su>;IGNORE;IGNORE
+<Zu> <Su>;<Zu>;IGNORE;IGNORE
+<zu> <Su>;<zu>;IGNORE;IGNORE
+<Se> <Se>;<Se>;IGNORE;IGNORE
+<se> <Se>;<se>;IGNORE;IGNORE
+<Ze> <Se>;<Ze>;IGNORE;IGNORE
+<ze> <Se>;<ze>;IGNORE;IGNORE
+<So> <So>;<So>;IGNORE;IGNORE
+<so> <So>;<so>;IGNORE;IGNORE
+<Zo> <So>;<Zo>;IGNORE;IGNORE
+<zo> <So>;<zo>;IGNORE;IGNORE
+<Ta> <Ta>;<Ta>;IGNORE;IGNORE
+<ta> <Ta>;<ta>;IGNORE;IGNORE
+<Da> <Ta>;<Da>;IGNORE;IGNORE
+<da> <Ta>;<da>;IGNORE;IGNORE
+<Ti> <Ti>;<Ti>;IGNORE;IGNORE
+<ti> <Ti>;<ti>;IGNORE;IGNORE
+<Di> <Ti>;<Di>;IGNORE;IGNORE
+<di> <Ti>;<di>;IGNORE;IGNORE
+<TU> <TU>;<TU>;IGNORE;IGNORE
+<tU> <TU>;<tU>;IGNORE;IGNORE
+<Tu> <TU>;<Tu>;IGNORE;IGNORE
+<tu> <TU>;<tu>;IGNORE;IGNORE
+<Du> <TU>;<Du>;IGNORE;IGNORE
+<du> <TU>;<du>;IGNORE;IGNORE
+<Te> <Te>;<Te>;IGNORE;IGNORE
+<te> <Te>;<te>;IGNORE;IGNORE
+<De> <Te>;<De>;IGNORE;IGNORE
+<de> <Te>;<de>;IGNORE;IGNORE
+<To> <To>;<To>;IGNORE;IGNORE
+<to> <To>;<to>;IGNORE;IGNORE
+<Do> <To>;<Do>;IGNORE;IGNORE
+<do> <To>;<do>;IGNORE;IGNORE
+<Na> <Na>;<Na>;IGNORE;IGNORE
+<na> <Na>;<na>;IGNORE;IGNORE
+<Ni> <Ni>;<Ni>;IGNORE;IGNORE
+<ni> <Ni>;<ni>;IGNORE;IGNORE
+<Nu> <Nu>;<Nu>;IGNORE;IGNORE
+<nu> <Nu>;<nu>;IGNORE;IGNORE
+<Ne> <Ne>;<Ne>;IGNORE;IGNORE
+<ne> <Ne>;<ne>;IGNORE;IGNORE
+<No> <No>;<No>;IGNORE;IGNORE
+<no> <No>;<no>;IGNORE;IGNORE
+<Ha> <Ha>;<Ha>;IGNORE;IGNORE
+<ha> <Ha>;<ha>;IGNORE;IGNORE
+<Ba> <Ha>;<Ba>;IGNORE;IGNORE
+<ba> <Ha>;<ba>;IGNORE;IGNORE
+<Pa> <Ha>;<Pa>;IGNORE;IGNORE
+<pa> <Ha>;<pa>;IGNORE;IGNORE
+<Hi> <Hi>;<Hi>;IGNORE;IGNORE
+<hi> <Hi>;<hi>;IGNORE;IGNORE
+<Bi> <Hi>;<Bi>;IGNORE;IGNORE
+<bi> <Hi>;<bi>;IGNORE;IGNORE
+<Pi> <Hi>;<Pi>;IGNORE;IGNORE
+<pi> <Hi>;<pi>;IGNORE;IGNORE
+<Hu> <Hu>;<Hu>;IGNORE;IGNORE
+<hu> <Hu>;<hu>;IGNORE;IGNORE
+<Bu> <Hu>;<Bu>;IGNORE;IGNORE
+<bu> <Hu>;<bu>;IGNORE;IGNORE
+<Pu> <Hu>;<Pu>;IGNORE;IGNORE
+<pu> <Hu>;<pu>;IGNORE;IGNORE
+<He> <He>;<He>;IGNORE;IGNORE
+<he> <He>;<he>;IGNORE;IGNORE
+<Be> <He>;<Be>;IGNORE;IGNORE
+<be> <He>;<be>;IGNORE;IGNORE
+<Pe> <He>;<Pe>;IGNORE;IGNORE
+<pe> <He>;<pe>;IGNORE;IGNORE
+<Ho> <Ho>;<Ho>;IGNORE;IGNORE
+<ho> <Ho>;<ho>;IGNORE;IGNORE
+<Bo> <Ho>;<Bo>;IGNORE;IGNORE
+<bo> <Ho>;<bo>;IGNORE;IGNORE
+<Po> <Ho>;<Po>;IGNORE;IGNORE
+<po> <Ho>;<po>;IGNORE;IGNORE
+<Ma> <Ma>;<Ma>;IGNORE;IGNORE
+<ma> <Ma>;<ma>;IGNORE;IGNORE
+<Mi> <Mi>;<Mi>;IGNORE;IGNORE
+<mi> <Mi>;<mi>;IGNORE;IGNORE
+<Mu> <Mu>;<Mu>;IGNORE;IGNORE
+<mu> <Mu>;<mu>;IGNORE;IGNORE
+<Me> <Me>;<Me>;IGNORE;IGNORE
+<me> <Me>;<me>;IGNORE;IGNORE
+<Mo> <Mo>;<Mo>;IGNORE;IGNORE
+<mo> <Mo>;<mo>;IGNORE;IGNORE
+<YA> <YA>;<YA>;IGNORE;IGNORE
+<yA> <YA>;<yA>;IGNORE;IGNORE
+<Ya> <YA>;<Ya>;IGNORE;IGNORE
+<ya> <YA>;<ya>;IGNORE;IGNORE
+<YU> <YU>;<YU>;IGNORE;IGNORE
+<yU> <YU>;<yU>;IGNORE;IGNORE
+<Yu> <YU>;<Yu>;IGNORE;IGNORE
+<yu> <YU>;<yu>;IGNORE;IGNORE
+<YO> <YO>;<YO>;IGNORE;IGNORE
+<yO> <YO>;<yO>;IGNORE;IGNORE
+<Yo> <YO>;<Yo>;IGNORE;IGNORE
+<yo> <YO>;<yo>;IGNORE;IGNORE
+<Ra> <Ra>;<Ra>;IGNORE;IGNORE
+<ra> <Ra>;<ra>;IGNORE;IGNORE
+<Ri> <Ri>;<Ri>;IGNORE;IGNORE
+<ri> <Ri>;<ri>;IGNORE;IGNORE
+<Ru> <Ru>;<Ru>;IGNORE;IGNORE
+<ru> <Ru>;<ru>;IGNORE;IGNORE
+<Re> <Re>;<Re>;IGNORE;IGNORE
+<re> <Re>;<re>;IGNORE;IGNORE
+<Ro> <Ro>;<Ro>;IGNORE;IGNORE
+<ro> <Ro>;<ro>;IGNORE;IGNORE
+<WA> <WA>;<WA>;IGNORE;IGNORE
+<wA> <WA>;<wA>;IGNORE;IGNORE
+<Wa> <WA>;<Wa>;IGNORE;IGNORE
+<wa> <WA>;<wa>;IGNORE;IGNORE
+<Wi> <Wi>;<Wi>;IGNORE;IGNORE
+<wi> <Wi>;<wi>;IGNORE;IGNORE
+<We> <We>;<We>;IGNORE;IGNORE
+<we> <We>;<we>;IGNORE;IGNORE
+<Wo> <Wo>;<Wo>;IGNORE;IGNORE
+<wo> <Wo>;<wo>;IGNORE;IGNORE
+<N6> <N6>;<N6>;IGNORE;IGNORE
+<n5> <N6>;<n5>;IGNORE;IGNORE
+
+order_end
+
+END LC_COLLATE
+
+LC_CTYPE
+
+digit <0>;<1>;<2>;<3>;<4>;/
+ <5>;<6>;<7>;<8>;<9>
+
+xdigit <0>;<1>;<2>;<3>;<4>;/
+ <5>;<6>;<7>;<8>;<9>;/
+ <A>;<B>;<C>;<D>;<E>;<F>;/
+ <a>;<b>;<c>;<d>;<e>;<f>
+
+blank <SP>;<HT>;<NS>
+
+space <SP>;<LF>;<VT>;<FF>;/
+ <CR>;<HT>;<NS>
+
+upper <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!>;<A'>;/
+ <A/>>;<A?>;<A:>;<AA>;<AE>;<C,>;/
+ <E!>;<E'>;<E/>>;<E:>;<I!>;<I'>;/
+ <I/>>;<I:>;<D->;<N?>;<O!>;<O'>;/
+ <O/>>;<O?>;<O:>;<O//>;<U!>;/
+ <U'>;<U/>>;<U:>;<Y'>;<TH>;<A->;/
+ <A(>;<A;>;<C'>;<C/>>;<C.>;<C<>;/
+ <D<>;<D//>;<E->;<E(>;<E.>;<E;>;/
+ <E<>;<G/>>;<G(>;<G.>;<G,>;/
+ <H/>>;<H//>;<I?>;<I->;<I(>;/
+ <I;>;<I.>;<IJ>;<J/>>;<K,>;<L'>;/
+ <L,>;<L<>;<L.>;<L//>;<N'>;<N,>;/
+ <N<>;<NG>;<O->;<O(>;<O">;<OE>;/
+ <R'>;<R,>;<R<>;<S'>;<S/>>;<S,>;/
+ <S<>;<T,>;<T<>;<T//>;<U?>;<U->;/
+ <U(>;<U0>;<U">;<U;>;<W/>>;/
+ <Y/>>;<Y:>;<Z'>;<Z.>;<Z<>;<C2>;/
+ <F2>;<K2>;<O9>;<OI>;<U9>;<Z//>;/
+ <ED>;<A<>;<I<>;<O<>;<U<>;<U:->;/
+ <U:'>;<U:<>;<U:!>;<A1>;<A7>;/
+ <A3>;<G//>;<G<>;<K<>;<O;>;<O1>;/
+ <EZ>;<G'>;<AA'>;<AE'>;<O//'>;/
+ <A!!>;<A)>;<E!!>;<E)>;<I!!>;/
+ <I)>;<O!!>;<O)>;<R!!>;<R)>;/
+ <U!!>;<U)>;<A%>;<E%>;<Y%>;<I%>;/
+ <O%>;<U%>;<W%>;<A*>;<B*>;<G*>;/
+ <D*>;<E*>;<Z*>;<Y*>;<H*>;<I*>;/
+ <K*>;<L*>;<M*>;<N*>;<C*>;<O*>;/
+ <P*>;<R*>;<S*>;<T*>;<U*>;<F*>;/
+ <X*>;<Q*>;<W*>;<J*>;<V*>;<IO>;/
+ <D%>;<G%>;<IE>;<DS>;<II>;<YI>;/
+ <J%>;<LJ>;<NJ>;<Ts>;<KJ>;<V%>;/
+ <DZ>;<A=>;<B=>;<V=>;<G=>;<D=>;/
+ <E=>;<Z%>;<Z=>;<I=>;<J=>;<K=>;/
+ <L=>;<M=>;<N=>;<O=>;<P=>;<R=>;/
+ <S=>;<T=>;<U=>;<F=>;<H=>;<C=>;/
+ <C%>;<S%>;<Sc>;<=">;<Y=>;<%">;/
+ <JE>;<JU>;<JA>;<Y3>;<O3>;<F3>;/
+ <V3>;<C3>;<G3>;<A-0>;<B.>;/
+ <B-.>;<B_>;<C,'>;<D.>;<D-.>;/
+ <D_>;<D,>;<D-/>>;<E-!>;<E-'>;/
+ <E-/>>;<E-?>;<E,(>;<F.>;<G->;/
+ <H.>;<H-.>;<H:>;<H,>;<H-(>;/
+ <I-?>;<I:'>;<K'>;<K-.>;<K_>;/
+ <L-.>;<L--.>;<L_>;<L-/>>;<M'>;/
+ <M.>;<M-.>;<N.>;<N-.>;<N_>;/
+ <N-/>>;<O?'>;<O?:>;<O-!>;<O-'>;/
+ <P'>;<P.>;<R.>;<R-.>;<R--.>;/
+ <R_>;<S.>;<S-.>;<S'.>;<S<.>;/
+ <S.-.>;<T.>;<T-.>;<T_>;<T-/>>;/
+ <U--:>;<U-?>;<U-/>>;<U?'>;/
+ <U-:>;<V?>;<V-.>;<W!>;<W'>;/
+ <W:>;<W.>;<W-.>;<X.>;<X:>;<Y.>;/
+ <Z/>>;<Z-.>;<Z_>;<A-.>;<A2>;/
+ <A/>'>;<A/>!>;<A/>2>;<A/>?>;/
+ <A/>-.>;<A('>;<A(!>;<A(2>;/
+ <A(?>;<A(-.>;<E-.>;<E2>;<E?>;/
+ <E/>'>;<E/>!>;<E/>2>;<E/>?>;/
+ <E/>-.>;<I2>;<I-.>;<O-.>;<O2>;/
+ <O/>'>;<O/>!>;<O/>2>;<O/>?>;/
+ <O/>-.>;<O9'>;<O9!>;<O92>;/
+ <O9?>;<O9-.>;<U-.>;<U2>;<U9'>;/
+ <U9!>;<U92>;<U9?>;<U9-.>;<Y!>;/
+ <Y-.>;<Y2>;<Y?>;<A-o>;<B-o>;/
+ <C-o>;<D-o>;<E-o>;<F-o>;<G-o>;/
+ <H-o>;<I-o>;<J-o>;<K-o>;<L-o>;/
+ <M-o>;<N-o>;<O-o>;<P-o>;<Q-o>;/
+ <R-o>;<S-o>;<T-o>;<U-o>;<V-o>;/
+ <W-o>;<X-o>;<Y-o>;<Z-o>
+
+lower <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>;<ss>;<a!>;/
+ <a'>;<a/>>;<a?>;<a:>;<aa>;<ae>;/
+ <c,>;<e!>;<e'>;<e/>>;<e:>;<i!>;/
+ <i'>;<i/>>;<i:>;<d->;<n?>;<o!>;/
+ <o'>;<o/>>;<o?>;<o:>;<o//>;/
+ <u!>;<u'>;<u/>>;<u:>;<y'>;<th>;/
+ <y:>;<a->;<a(>;<a;>;<c'>;<c/>>;/
+ <c.>;<c<>;<d<>;<d//>;<e->;<e(>;/
+ <e.>;<e;>;<e<>;<g/>>;<g(>;<g.>;/
+ <g,>;<h/>>;<h//>;<i?>;<i->;/
+ <i(>;<i;>;<i.>;<ij>;<j/>>;<k,>;/
+ <kk>;<l'>;<l,>;<l<>;<l.>;<l//>;/
+ <n'>;<n,>;<n<>;<'n>;<ng>;<o->;/
+ <o(>;<o">;<oe>;<r'>;<r,>;<r<>;/
+ <s'>;<s/>>;<s,>;<s<>;<t,>;<t<>;/
+ <t//>;<u?>;<u->;<u(>;<u0>;<u">;/
+ <u;>;<w/>>;<y/>>;<z'>;<z.>;/
+ <z<>;<s1>;<c2>;<f2>;<k2>;<o9>;/
+ <oi>;<u9>;<z//>;<a<>;<i<>;<o<>;/
+ <u<>;<u:->;<u:'>;<u:<>;<u:!>;/
+ <a1>;<a7>;<a3>;<g//>;<g<>;<k<>;/
+ <o;>;<o1>;<ez>;<g'>;<aa'>;/
+ <ae'>;<o//'>;<a!!>;<a)>;<e!!>;/
+ <e)>;<i!!>;<i)>;<o!!>;<o)>;/
+ <r!!>;<r)>;<u!!>;<u)>;<ed>;/
+ <i3>;<a%>;<e%>;<y%>;<i%>;<u3>;/
+ <a*>;<b*>;<g*>;<d*>;<e*>;<z*>;/
+ <y*>;<h*>;<i*>;<k*>;<l*>;<m*>;/
+ <n*>;<c*>;<o*>;<p*>;<r*>;<*s>;/
+ <s*>;<t*>;<u*>;<f*>;<x*>;<q*>;/
+ <w*>;<j*>;<v*>;<o%>;<u%>;<w%>;/
+ <a=>;<b=>;<v=>;<g=>;<d=>;<e=>;/
+ <z%>;<z=>;<i=>;<j=>;<k=>;<l=>;/
+ <m=>;<n=>;<o=>;<p=>;<r=>;<s=>;/
+ <t=>;<u=>;<f=>;<h=>;<c=>;<c%>;/
+ <s%>;<sc>;<='>;<y=>;<%'>;<je>;/
+ <ju>;<ja>;<io>;<d%>;<g%>;<ie>;/
+ <ds>;<ii>;<yi>;<j%>;<lj>;<nj>;/
+ <ts>;<kj>;<v%>;<dz>;<y3>;<o3>;/
+ <f3>;<v3>;<c3>;<g3>;<a-0>;<b.>;/
+ <b-.>;<b_>;<c,'>;<d.>;<d-.>;/
+ <d_>;<d,>;<d-/>>;<e-!>;<e-'>;/
+ <e-/>>;<e-?>;<e,(>;<f.>;<g->;/
+ <h.>;<h-.>;<h:>;<h,>;<h-(>;/
+ <i-?>;<i:'>;<k'>;<k-.>;<k_>;/
+ <l-.>;<l--.>;<l_>;<l-/>>;<m'>;/
+ <m.>;<m-.>;<n.>;<n-.>;<n_>;/
+ <n-/>>;<o?'>;<o?:>;<o-!>;<o-'>;/
+ <p'>;<p.>;<r.>;<r-.>;<r--.>;/
+ <r_>;<s.>;<s-.>;<s'.>;<s<.>;/
+ <s.-.>;<t.>;<t-.>;<t_>;<t-/>>;/
+ <u--:>;<u-?>;<u-/>>;<u?'>;/
+ <u-:>;<v?>;<v-.>;<w!>;<w'>;/
+ <w:>;<w.>;<w-.>;<x.>;<x:>;<y.>;/
+ <z/>>;<z-.>;<z_>;<a-.>;<a2>;/
+ <a/>'>;<a/>!>;<a/>2>;<a/>?>;/
+ <a/>-.>;<a('>;<a(!>;<a(2>;/
+ <a(?>;<a(-.>;<e-.>;<e2>;<e?>;/
+ <e/>'>;<e/>!>;<e/>2>;<e/>?>;/
+ <e/>-.>;<i2>;<i-.>;<o-.>;<o2>;/
+ <o/>'>;<o/>!>;<o/>2>;<o/>?>;/
+ <o/>-.>;<o9'>;<o9!>;<o92>;/
+ <o9?>;<o9-.>;<u-.>;<u2>;<u9'>;/
+ <u9!>;<u92>;<u9?>;<u9-.>;<y!>;/
+ <y-.>;<y2>;<y?>;<nS>;<(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-o>;<b-o>;<c-o>;<d-o>;<e-o>;/
+ <f-o>;<g-o>;<h-o>;<i-o>;<j-o>;/
+ <k-o>;<l-o>;<m-o>;<n-o>;<o-o>;/
+ <p-o>;<q-o>;<r-o>;<s-o>;<t-o>;/
+ <u-o>;<v-o>;<w-o>;<x-o>;<y-o>;/
+ <z-o>;<ff>;<fi>;<fl>;<ffi>;/
+ <ffl>;<ft>;<st>
+
+alpha <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>;<-->;<A!>;<A'>;/
+ <A/>>;<A?>;<A:>;<AA>;<AE>;<C,>;/
+ <E!>;<E'>;<E/>>;<E:>;<I!>;<I'>;/
+ <I/>>;<I:>;<D->;<N?>;<O!>;<O'>;/
+ <O/>>;<O?>;<O:>;<O//>;<U!>;/
+ <U'>;<U/>>;<U:>;<Y'>;<TH>;<ss>;/
+ <a!>;<a'>;<a/>>;<a?>;<a:>;<aa>;/
+ <ae>;<c,>;<e!>;<e'>;<e/>>;<e:>;/
+ <i!>;<i'>;<i/>>;<i:>;<d->;<n?>;/
+ <o!>;<o'>;<o/>>;<o?>;<o:>;/
+ <o//>;<u!>;<u'>;<u/>>;<u:>;/
+ <y'>;<th>;<y:>;<A->;<a->;<A(>;/
+ <a(>;<A;>;<a;>;<C'>;<c'>;<C/>>;/
+ <c/>>;<C.>;<c.>;<C<>;<c<>;<D<>;/
+ <d<>;<D//>;<d//>;<E->;<e->;/
+ <E(>;<e(>;<E.>;<e.>;<E;>;<e;>;/
+ <E<>;<e<>;<G/>>;<g/>>;<G(>;/
+ <g(>;<G.>;<g.>;<G,>;<g,>;<H/>>;/
+ <h/>>;<H//>;<h//>;<I?>;<i?>;/
+ <I->;<i->;<I(>;<i(>;<I;>;<i;>;/
+ <I.>;<i.>;<IJ>;<ij>;<J/>>;/
+ <j/>>;<K,>;<k,>;<kk>;<L'>;<l'>;/
+ <L,>;<l,>;<L<>;<l<>;<L.>;<l.>;/
+ <L//>;<l//>;<N'>;<n'>;<N,>;/
+ <n,>;<N<>;<n<>;<'n>;<NG>;<ng>;/
+ <O->;<o->;<O(>;<o(>;<O">;<o">;/
+ <OE>;<oe>;<R'>;<r'>;<R,>;<r,>;/
+ <R<>;<r<>;<S'>;<s'>;<S/>>;/
+ <s/>>;<S,>;<s,>;<S<>;<s<>;<T,>;/
+ <t,>;<T<>;<t<>;<T//>;<t//>;/
+ <U?>;<u?>;<U->;<u->;<U(>;<u(>;/
+ <U0>;<u0>;<U">;<u">;<U;>;<u;>;/
+ <W/>>;<w/>>;<Y/>>;<y/>>;<Y:>;/
+ <Z'>;<z'>;<Z.>;<z.>;<Z<>;<z<>;/
+ <s1>;<C2>;<c2>;<F2>;<f2>;<K2>;/
+ <k2>;<O9>;<o9>;<OI>;<oi>;<yr>;/
+ <U9>;<u9>;<Z//>;<z//>;<ED>;/
+ <A<>;<a<>;<I<>;<i<>;<O<>;<o<>;/
+ <U<>;<u<>;<U:->;<u:->;<U:'>;/
+ <u:'>;<U:<>;<u:<>;<U:!>;<u:!>;/
+ <A1>;<a1>;<A7>;<a7>;<A3>;<a3>;/
+ <G//>;<g//>;<G<>;<g<>;<K<>;/
+ <k<>;<O;>;<o;>;<O1>;<o1>;<EZ>;/
+ <ez>;<G'>;<g'>;<AA'>;<aa'>;/
+ <AE'>;<ae'>;<O//'>;<o//'>;/
+ <A!!>;<a!!>;<A)>;<a)>;<E!!>;/
+ <e!!>;<E)>;<e)>;<I!!>;<i!!>;/
+ <I)>;<i)>;<O!!>;<o!!>;<O)>;/
+ <o)>;<R!!>;<r!!>;<R)>;<r)>;/
+ <U!!>;<u!!>;<U)>;<u)>;<ed>;/
+ <;S>;<1/>>;<1!>;<A%>;<E%>;<Y%>;/
+ <I%>;<O%>;<U%>;<W%>;<i3>;<A*>;/
+ <B*>;<G*>;<D*>;<E*>;<Z*>;<Y*>;/
+ <H*>;<I*>;<K*>;<L*>;<M*>;<N*>;/
+ <C*>;<O*>;<P*>;<R*>;<S*>;<T*>;/
+ <U*>;<F*>;<X*>;<Q*>;<W*>;<J*>;/
+ <V*>;<a%>;<e%>;<y%>;<i%>;<u3>;/
+ <a*>;<b*>;<g*>;<d*>;<e*>;<z*>;/
+ <y*>;<h*>;<i*>;<k*>;<l*>;<m*>;/
+ <n*>;<c*>;<o*>;<p*>;<r*>;<*s>;/
+ <s*>;<t*>;<u*>;<f*>;<x*>;<q*>;/
+ <w*>;<j*>;<v*>;<o%>;<u%>;<w%>;/
+ <IO>;<D%>;<G%>;<IE>;<DS>;<II>;/
+ <YI>;<J%>;<LJ>;<NJ>;<Ts>;<KJ>;/
+ <V%>;<DZ>;<A=>;<B=>;<V=>;<G=>;/
+ <D=>;<E=>;<Z%>;<Z=>;<I=>;<J=>;/
+ <K=>;<L=>;<M=>;<N=>;<O=>;<P=>;/
+ <R=>;<S=>;<T=>;<U=>;<F=>;<H=>;/
+ <C=>;<C%>;<S%>;<Sc>;<=">;<Y=>;/
+ <%">;<JE>;<JU>;<JA>;<a=>;<b=>;/
+ <v=>;<g=>;<d=>;<e=>;<z%>;<z=>;/
+ <i=>;<j=>;<k=>;<l=>;<m=>;<n=>;/
+ <o=>;<p=>;<r=>;<s=>;<t=>;<u=>;/
+ <f=>;<h=>;<c=>;<c%>;<s%>;<sc>;/
+ <='>;<y=>;<%'>;<je>;<ju>;<ja>;/
+ <io>;<d%>;<g%>;<ie>;<ds>;<ii>;/
+ <yi>;<j%>;<lj>;<nj>;<ts>;<kj>;/
+ <v%>;<dz>;<Y3>;<y3>;<O3>;<o3>;/
+ <F3>;<f3>;<V3>;<v3>;<C3>;<c3>;/
+ <G3>;<g3>;<A+>;<B+>;<G+>;<D+>;/
+ <H+>;<W+>;<Z+>;<X+>;<Tj>;<J+>;/
+ <K%>;<K+>;<L+>;<M%>;<M+>;<N%>;/
+ <N+>;<S+>;<E+>;<P%>;<P+>;<Zj>;/
+ <ZJ>;<Q+>;<R+>;<Sh>;<T+>;<H'>;/
+ <aM>;<aH>;<wH>;<ah>;<yH>;<a+>;/
+ <b+>;<tm>;<t+>;<tk>;<g+>;<hk>;/
+ <x+>;<d+>;<dk>;<r+>;<z+>;<s+>;/
+ <sn>;<c+>;<dd>;<tj>;<zH>;<e+>;/
+ <i+>;<f+>;<q+>;<k+>;<l+>;<m+>;/
+ <n+>;<h+>;<w+>;<j+>;<y+>;<aS>;/
+ <p+>;<hH>;<tc>;<zj>;<v+>;<gf>;/
+ <A-0>;<a-0>;<B.>;<b.>;<B-.>;/
+ <b-.>;<B_>;<b_>;<C,'>;<c,'>;/
+ <D.>;<d.>;<D-.>;<d-.>;<D_>;/
+ <d_>;<D,>;<d,>;<D-/>>;<d-/>>;/
+ <E-!>;<e-!>;<E-'>;<e-'>;<E-/>>;/
+ <e-/>>;<E-?>;<e-?>;<E,(>;<e,(>;/
+ <F.>;<f.>;<G->;<g->;<H.>;<h.>;/
+ <H-.>;<h-.>;<H:>;<h:>;<H,>;/
+ <h,>;<H-(>;<h-(>;<I-?>;<i-?>;/
+ <I:'>;<i:'>;<K'>;<k'>;<K-.>;/
+ <k-.>;<K_>;<k_>;<L-.>;<l-.>;/
+ <L--.>;<l--.>;<L_>;<l_>;<L-/>>;/
+ <l-/>>;<M'>;<m'>;<M.>;<m.>;/
+ <M-.>;<m-.>;<N.>;<n.>;<N-.>;/
+ <n-.>;<N_>;<n_>;<N-/>>;<n-/>>;/
+ <O?'>;<o?'>;<O?:>;<o?:>;<O-!>;/
+ <o-!>;<O-'>;<o-'>;<P'>;<p'>;/
+ <P.>;<p.>;<R.>;<r.>;<R-.>;/
+ <r-.>;<R--.>;<r--.>;<R_>;<r_>;/
+ <S.>;<s.>;<S-.>;<s-.>;<S'.>;/
+ <s'.>;<S<.>;<s<.>;<S.-.>;/
+ <s.-.>;<T.>;<t.>;<T-.>;<t-.>;/
+ <T_>;<t_>;<T-/>>;<t-/>>;<U--:>;/
+ <u--:>;<U-?>;<u-?>;<U-/>>;/
+ <u-/>>;<U?'>;<u?'>;<U-:>;<u-:>;/
+ <V?>;<v?>;<V-.>;<v-.>;<W!>;/
+ <w!>;<W'>;<w'>;<W:>;<w:>;<W.>;/
+ <w.>;<W-.>;<w-.>;<X.>;<x.>;/
+ <X:>;<x:>;<Y.>;<y.>;<Z/>>;/
+ <z/>>;<Z-.>;<z-.>;<Z_>;<z_>;/
+ <A-.>;<a-.>;<A2>;<a2>;<A/>'>;/
+ <a/>'>;<A/>!>;<a/>!>;<A/>2>;/
+ <a/>2>;<A/>?>;<a/>?>;<A/>-.>;/
+ <a/>-.>;<A('>;<a('>;<A(!>;/
+ <a(!>;<A(2>;<a(2>;<A(?>;<a(?>;/
+ <A(-.>;<a(-.>;<E-.>;<e-.>;<E2>;/
+ <e2>;<E?>;<e?>;<E/>'>;<e/>'>;/
+ <E/>!>;<e/>!>;<E/>2>;<e/>2>;/
+ <E/>?>;<e/>?>;<E/>-.>;<e/>-.>;/
+ <I2>;<i2>;<I-.>;<i-.>;<O-.>;/
+ <o-.>;<O2>;<o2>;<O/>'>;<o/>'>;/
+ <O/>!>;<o/>!>;<O/>2>;<o/>2>;/
+ <O/>?>;<o/>?>;<O/>-.>;<o/>-.>;/
+ <O9'>;<o9'>;<O9!>;<o9!>;<O92>;/
+ <o92>;<O9?>;<o9?>;<O9-.>;/
+ <o9-.>;<U-.>;<u-.>;<U2>;<u2>;/
+ <U9'>;<u9'>;<U9!>;<u9!>;<U92>;/
+ <u92>;<U9?>;<u9?>;<U9-.>;/
+ <u9-.>;<Y!>;<y!>;<Y-.>;<y-.>;/
+ <Y2>;<y2>;<Y?>;<y?>;<nS>;<(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-o>;<B-o>;<C-o>;<D-o>;<E-o>;/
+ <F-o>;<G-o>;<H-o>;<I-o>;<J-o>;/
+ <K-o>;<L-o>;<M-o>;<N-o>;<O-o>;/
+ <P-o>;<Q-o>;<R-o>;<S-o>;<T-o>;/
+ <U-o>;<V-o>;<W-o>;<X-o>;<Y-o>;/
+ <Z-o>;<a-o>;<b-o>;<c-o>;<d-o>;/
+ <e-o>;<f-o>;<g-o>;<h-o>;<i-o>;/
+ <j-o>;<k-o>;<l-o>;<m-o>;<n-o>;/
+ <o-o>;<p-o>;<q-o>;<r-o>;<s-o>;/
+ <t-o>;<u-o>;<v-o>;<w-o>;<x-o>;/
+ <y-o>;<z-o>;<A5>;<a5>;<I5>;/
+ <i5>;<U5>;<u5>;<E5>;<e5>;<O5>;/
+ <o5>;<ka>;<ga>;<ki>;<gi>;<ku>;/
+ <gu>;<ke>;<ge>;<ko>;<go>;<sa>;/
+ <za>;<si>;<zi>;<su>;<zu>;<se>;/
+ <ze>;<so>;<zo>;<ta>;<da>;<ti>;/
+ <di>;<tU>;<tu>;<du>;<te>;<de>;/
+ <to>;<do>;<na>;<ni>;<nu>;<ne>;/
+ <no>;<ha>;<ba>;<pa>;<hi>;<bi>;/
+ <pi>;<hu>;<bu>;<pu>;<he>;<be>;/
+ <pe>;<ho>;<bo>;<po>;<ma>;<mi>;/
+ <mu>;<me>;<mo>;<yA>;<ya>;<yU>;/
+ <yu>;<yO>;<yo>;<ra>;<ri>;<ru>;/
+ <re>;<ro>;<wA>;<wa>;<wi>;<we>;/
+ <wo>;<n5>;<vu>;<a6>;<A6>;<i6>;/
+ <I6>;<u6>;<U6>;<e6>;<E6>;<o6>;/
+ <O6>;<Ka>;<Ga>;<Ki>;<Gi>;<Ku>;/
+ <Gu>;<Ke>;<Ge>;<Ko>;<Go>;<Sa>;/
+ <Za>;<Si>;<Zi>;<Su>;<Zu>;<Se>;/
+ <Ze>;<So>;<Zo>;<Ta>;<Da>;<Ti>;/
+ <Di>;<TU>;<Tu>;<Du>;<Te>;<De>;/
+ <To>;<Do>;<Na>;<Ni>;<Nu>;<Ne>;/
+ <No>;<Ha>;<Ba>;<Pa>;<Hi>;<Bi>;/
+ <Pi>;<Hu>;<Bu>;<Pu>;<He>;<Be>;/
+ <Pe>;<Ho>;<Bo>;<Po>;<Ma>;<Mi>;/
+ <Mu>;<Me>;<Mo>;<YA>;<Ya>;<YU>;/
+ <Yu>;<YO>;<Yo>;<Ra>;<Ri>;<Ru>;/
+ <Re>;<Ro>;<WA>;<Wa>;<Wi>;<We>;/
+ <Wo>;<N6>;<Vu>;<KA>;<KE>;<Va>;/
+ <Vi>;<Ve>;<Vo>;<ff>;<fi>;<fl>;/
+ <ffi>;<ffl>;<ft>;<st>;<aM.>;/
+ <aH.>;<ah.>;<a+->;<a+.>;<b+->;/
+ <b+.>;<b+,>;<b+;>;<tm->;<tm.>;/
+ <t+->;<t+.>;<t+,>;<t+;>;<tk->;/
+ <tk.>;<tk,>;<tk;>;<g+->;<g+.>;/
+ <g+,>;<g+;>;<hk->;<hk.>;<hk,>;/
+ <hk;>;<x+->;<x+.>;<x+,>;<x+;>;/
+ <d+->;<d+.>;<dk->;<dk.>;<r+->;/
+ <r+.>;<z+->;<z+.>;<s+->;<s+.>;/
+ <s+,>;<s+;>;<sn->;<sn.>;<sn,>;/
+ <sn;>;<c+->;<c+.>;<c+,>;<c+;>;/
+ <dd->;<dd.>;<dd,>;<dd;>;<tj->;/
+ <tj.>;<tj,>;<tj;>;<zH->;<zH.>;/
+ <zH,>;<zH;>;<e+->;<e+.>;<e+,>;/
+ <e+;>;<i+->;<i+.>;<i+,>;<i+;>;/
+ <f+->;<f+.>;<f+,>;<f+;>;<q+->;/
+ <q+.>;<q+,>;<q+;>;<k+->;<k+.>;/
+ <k+,>;<k+;>;<l+->;<l+.>;<l+,>;/
+ <l+;>;<m+->;<m+.>;<m+,>;<m+;>;/
+ <n+->;<n+.>;<n+,>;<n+;>;<h+->;/
+ <h+.>;<h+,>;<h+;>;<w+->;<w+.>;/
+ <j+->;<j+.>;<y+->;<y+.>;<y+,>;/
+ <y+;>;<lM->;<lM.>;<lH->;<lH.>;/
+ <lh->;<lh.>;<la->;<la.>;<a+:>
+
+cntrl <NU>;<SH>;<SX>;<EX>;<ET>;<EQ>;/
+ <AK>;<BL>;<BS>;<HT>;<LF>;<VT>;/
+ <FF>;<CR>;<SO>;<SI>;<DL>;<D1>;/
+ <D2>;<D3>;<D4>;<NK>;<SY>;<EB>;/
+ <CN>;<EM>;<SB>;<EC>;<FS>;<GS>;/
+ <RS>;<US>;<DT>;<PA>;<HO>;<BH>;/
+ <NH>;<IN>;<NL>;<SA>;<ES>;<HS>;/
+ <HJ>;<VS>;<PD>;<PU>;<RI>;<S2>;/
+ <S3>;<DC>;<P1>;<P2>;<TS>;<CC>;/
+ <MW>;<SG>;<EG>;<SS>;<GC>;<SC>;/
+ <CI>;<ST>;<OC>;<PM>;<AC>
+
+punct <!>;<">;<Nb>;<DO>;<%>;<&>;<'>;/
+ <(>;<)>;<*>;<+>;<,>;<->;<.>;/
+ <//>;<:>;<;>;<<>;<=>;</>>;<?>;/
+ <At>;<<(>;<////>;<)/>>;<'/>>;/
+ <_>;<'!>;<(!>;<!!>;<!)>;<'?>;/
+ <!I>;<Ct>;<Pd>;<Cu>;<Ye>;<BB>;/
+ <SE>;<':>;<Co>;<-a>;<<<>;<NO>;/
+ <Rg>;<'m>;<DG>;<+->;<2S>;<3S>;/
+ <''>;<My>;<PI>;<.M>;<',>;<1S>;/
+ <-o>;</>/>>;<14>;<12>;<34>;/
+ <?I>;<*X>;<-:>;<'<>;<'(>;<'.>;/
+ <'0>;<';>;<1?>;<'">;<'G>;<,G>;/
+ <j3>;<?%>;<'*>;<'%>;<.*>;<b3>;/
+ <,+>;<;+>;<?+>;<++>;<:+>;<"+>;/
+ <=+>;<//+>;<'+>;<1+>;<3+>;<0+>;/
+ <0a>;<1a>;<2a>;<3a>;<4a>;<5a>;/
+ <6a>;<7a>;<8a>;<9a>;<,,>;<?*>;/
+ <?:>;<,!>;<,'>;<?,>;<;!>;<;'>;/
+ <?;>;<!:>;<!*>;<;;>;<1N>;<1M>;/
+ <3M>;<4M>;<6M>;<LR>;<RL>;<1T>;/
+ <1H>;<-1>;<-N>;<-M>;<-3>;<!2>;/
+ <=2>;<'6>;<'9>;<.9>;<9'>;<"6>;/
+ <"9>;<:9>;<9">;<//->;<//=>;/
+ <Sb>;<..>;<.3>;<%0>;<1'>;<2'>;/
+ <3'>;<1">;<2">;<3">;<Ca>;<<1>;/
+ </>1>;<:X>;<!*2>;<'->;<0S>;/
+ <4S>;<5S>;<6S>;<7S>;<8S>;<9S>;/
+ <+S>;<-S>;<=S>;<(S>;<)S>;<0s>;/
+ <1s>;<2s>;<3s>;<4s>;<5s>;<6s>;/
+ <7s>;<8s>;<9s>;<+s>;<-s>;<=s>;/
+ <(s>;<)s>;<Ff>;<Li>;<Pt>;<W=>;/
+ <oC>;<co>;<oF>;<N0>;<PO>;<Rx>;/
+ <SM>;<TM>;<Om>;<AO>;<13>;<23>;/
+ <15>;<25>;<35>;<45>;<16>;<56>;/
+ <18>;<38>;<58>;<78>;<1R>;<2R>;/
+ <3R>;<4R>;<5R>;<6R>;<7R>;<8R>;/
+ <9R>;<aR>;<bR>;<cR>;<50R>;/
+ <100R>;<500R>;<1000R>;<1r>;/
+ <2r>;<3r>;<4r>;<5r>;<6r>;<7r>;/
+ <8r>;<9r>;<ar>;<br>;<cr>;<50r>;/
+ <100r>;<500r>;<1000r>;/
+ <1000RCD>;<5000R>;<10000R>;/
+ <<->;<-!>;<-/>>;<-v>;<</>>;/
+ <UD>;<<!!>;</////>>;<!!/>>;/
+ <<////>;<UD->;</>V>;<<=>;<=/>>;/
+ <==>;<FA>;<dP>;<TE>;<//0>;<DE>;/
+ <NB>;<(->;<-)>;<*P>;<+Z>;<-2>;/
+ <-+>;<.+>;<//f>;<*->;<Ob>;<sb>;/
+ <RT>;<0(>;<00>;<-L>;<-V>;<PP>;/
+ <AN>;<OR>;<(U>;<)U>;<In>;<DI>;/
+ <Io>;<.:>;<:.>;<:R>;<::>;<?1>;/
+ <CG>;<?->;<?=>;<?2>;<=?>;<HI>;/
+ <!=>;<=3>;<=<>;</>=>;<<*>;/
+ <*/>>;<!<>;<!/>>;<(C>;<)C>;/
+ <(_>;<)_>;<0.>;<02>;<-T>;<.P>;/
+ <:3>;<Eh>;<<7>;</>7>;<7<>;/
+ <7/>>;<NI>;<(A>;<TR>;<Iu>;<Il>;/
+ <<//>;<///>>;<Vs>;<1h>;<3h>;/
+ <2h>;<4h>;<1j>;<2j>;<3j>;<4j>;/
+ <1-o>;<2-o>;<3-o>;<4-o>;<5-o>;/
+ <6-o>;<7-o>;<8-o>;<9-o>;<10-o>;/
+ <11-o>;<12-o>;<13-o>;<14-o>;/
+ <15-o>;<16-o>;<17-o>;<18-o>;/
+ <19-o>;<20-o>;<(1)>;<(2)>;/
+ <(3)>;<(4)>;<(5)>;<(6)>;<(7)>;/
+ <(8)>;<(9)>;<(10)>;<(11)>;/
+ <(12)>;<(13)>;<(14)>;<(15)>;/
+ <(16)>;<(17)>;<(18)>;<(19)>;/
+ <(20)>;<1.>;<2.>;<3.>;<4.>;/
+ <5.>;<6.>;<7.>;<8.>;<9.>;<10.>;/
+ <11.>;<12.>;<13.>;<14.>;<15.>;/
+ <16.>;<17.>;<18.>;<19.>;<20.>;/
+ <0-o>;<hh>;<HH>;<vv>;<VV>;<3->;/
+ <3_>;<3!>;<3//>;<4->;<4_>;<4!>;/
+ <4//>;<dr>;<dR>;<Dr>;<DR>;<dl>;/
+ <dL>;<Dl>;<LD>;<ur>;<uR>;<Ur>;/
+ <UR>;<ul>;<uL>;<Ul>;<UL>;<vr>;/
+ <vR>;<Udr>;<uDr>;<Vr>;<UdR>;/
+ <uDR>;<VR>;<vl>;<vL>;<Udl>;/
+ <uDl>;<Vl>;<UdL>;<uDL>;<VL>;/
+ <dh>;<dLr>;<dlR>;<dH>;<Dh>;/
+ <DLr>;<DlR>;<DH>;<uh>;<uLr>;/
+ <ulR>;<uH>;<Uh>;<ULr>;<UlR>;/
+ <UH>;<vh>;<vLr>;<vlR>;<vH>;/
+ <Udh>;<uDh>;<Vh>;<UdLr>;<UdlR>;/
+ <uDLr>;<uDlR>;<UdH>;<uDH>;/
+ <VLr>;<VlR>;<VH>;<FD>;<BD>;/
+ <TB>;<LB>;<FB>;<lB>;<RB>;<.S>;/
+ <:S>;<?S>;<fS>;<OS>;<RO>;<Rr>;/
+ <RF>;<RY>;<RH>;<RZ>;<RK>;<RX>;/
+ <sB>;<SR>;<Or>;<UT>;<uT>;<Tr>;/
+ <PR>;<Dt>;<dT>;<Tl>;<PL>;<Db>;/
+ <Dw>;<LZ>;<0m>;<0o>;<0M>;<0L>;/
+ <0R>;<Sn>;<Ic>;<Fd>;<Bd>;<Ci>;/
+ <*2>;<*1>;<TEL>;<tel>;<<H>;/
+ </>H>;<0u>;<0U>;<SU>;<Fm>;<Ml>;/
+ <cS>;<cH>;<cD>;<cC>;<cS->;/
+ <cH->;<cD->;<cC->;<Md>;<M8>;/
+ <M2>;<M16>;<Mb>;<Mx>;<MX>;<OK>;/
+ <XX>;<-X>;<IS>;<,_>;<._>;<+">;/
+ <JIS>;<*_>;<;_>;<0_>;<<+>;/
+ </>+>;<<'>;</>'>;<<">;</>">;/
+ <(">;<)">;<=T>;<=_>;<('>;<)'>;/
+ <(I>;<)I>;<-?>;<=T:)>;<"5>;/
+ <05>;<*5>;<+5>;<.6>;<-6>;<*6>;/
+ <+6>;<(JU)>;<1c>;<2c>;<3c>;/
+ <4c>;<5c>;<6c>;<7c>;<8c>;<9c>;/
+ <10c>;<KSC>;<am>;<pm>;<3+;>;/
+ <"3>;<"1>;<"!>;<"'>;<"/>>;<"?>;/
+ <"->;<"(>;<".>;<":>;<"0>;<",>;/
+ <"_>;<"">;<";>;<"<>;<"=>;<"//>;/
+ <"p>;<"d>;<"i>;<+_>;<Tel>;<UA>;/
+ <UB>
+
+tolower (<A>,<a>);(<A!>,<a!>);/
+ (<A!!>,<a!!>);(<A'>,<a'>);/
+ (<A(>,<a(>);(<A(!>,<a(!>);/
+ (<A('>,<a('>);(<A(-.>,<a(-.>);/
+ (<A(2>,<a(2>);(<A(?>,<a(?>);/
+ (<A)>,<a)>);(<A->,<a->);/
+ (<A-.>,<a-.>);(<A-0>,<a-0>);/
+ (<A-o>,<a-o>);(<A1>,<a1>);/
+ (<A2>,<a2>);(<A3>,<a3>);/
+ (<A7>,<a7>);(<A:>,<a:>);/
+ (<A;>,<a;>);(<A<>,<a<>);/
+ (<A/>>,<a/>>);(<A/>!>,<a/>!>);/
+ (<A/>'>,<a/>'>);/
+ (<A/>-.>,<a/>-.>);/
+ (<A/>2>,<a/>2>);/
+ (<A/>?>,<a/>?>);(<A?>,<a?>);/
+ (<AA>,<aa>);(<AA'>,<aa'>);/
+ (<AE>,<ae>);(<AE'>,<ae'>);/
+ (<B>,<b>);(<B-.>,<b-.>);/
+ (<B-o>,<b-o>);(<B.>,<b.>);/
+ (<B_>,<b_>);(<C>,<c>);/
+ (<C'>,<c'>);(<C,>,<c,>);/
+ (<C,'>,<c,'>);(<C-o>,<c-o>);/
+ (<C.>,<c.>);(<C2>,<c2>);/
+ (<C<>,<c<>);(<C/>>,<c/>>);/
+ (<D>,<d>);(<D,>,<d,>);/
+ (<D->,<d->);(<D-.>,<d-.>);/
+ (<D-/>>,<d-/>>);(<D-o>,<d-o>);/
+ (<D.>,<d.>);(<D//>,<d//>);/
+ (<D<>,<d<>);(<D_>,<d_>);/
+ (<E>,<e>);(<E!>,<e!>);/
+ (<E!!>,<e!!>);(<E'>,<e'>);/
+ (<E(>,<e(>);(<E)>,<e)>);/
+ (<E,(>,<e,(>);(<E->,<e->);/
+ (<E-!>,<e-!>);(<E-'>,<e-'>);/
+ (<E-.>,<e-.>);(<E-/>>,<e-/>>);/
+ (<E-?>,<e-?>);(<E-o>,<e-o>);/
+ (<E.>,<e.>);(<E2>,<e2>);/
+ (<E:>,<e:>);(<E;>,<e;>);/
+ (<E<>,<e<>);(<E/>>,<e/>>);/
+ (<E/>!>,<e/>!>);/
+ (<E/>'>,<e/>'>);/
+ (<E/>-.>,<e/>-.>);/
+ (<E/>2>,<e/>2>);/
+ (<E/>?>,<e/>?>);(<E?>,<e?>);/
+ (<ED>,<ed>);(<EZ>,<ez>);/
+ (<F>,<f>);(<F-o>,<f-o>);/
+ (<F.>,<f.>);(<F2>,<f2>);/
+ (<G>,<g>);(<G'>,<g'>);/
+ (<G(>,<g(>);(<G,>,<g,>);/
+ (<G->,<g->);(<G-o>,<g-o>);/
+ (<G.>,<g.>);(<G//>,<g//>);/
+ (<G<>,<g<>);(<G/>>,<g/>>);/
+ (<H>,<h>);(<H,>,<h,>);/
+ (<H-(>,<h-(>);(<H-.>,<h-.>);/
+ (<H-o>,<h-o>);(<H.>,<h.>);/
+ (<H//>,<h//>);(<H:>,<h:>);/
+ (<H/>>,<h/>>);(<I>,<i>);/
+ (<I!>,<i!>);(<I!!>,<i!!>);/
+ (<I'>,<i'>);(<I(>,<i(>);/
+ (<I)>,<i)>);(<I->,<i->);/
+ (<I-.>,<i-.>);(<I-?>,<i-?>);/
+ (<I-o>,<i-o>);(<I.>,<i.>);/
+ (<I2>,<i2>);(<I:>,<i:>);/
+ (<I:'>,<i:'>);(<I;>,<i;>);/
+ (<I<>,<i<>);(<I/>>,<i/>>);/
+ (<I?>,<i?>);(<IJ>,<ij>);/
+ (<J>,<j>);(<J-o>,<j-o>);/
+ (<J/>>,<j/>>);(<K>,<k>);/
+ (<K'>,<k'>);(<K,>,<k,>);/
+ (<K-.>,<k-.>);(<K-o>,<k-o>);/
+ (<K2>,<k2>);(<K<>,<k<>);/
+ (<K_>,<k_>);(<L>,<l>);/
+ (<L'>,<l'>);(<L,>,<l,>);/
+ (<L--.>,<l--.>);(<L-.>,<l-.>);/
+ (<L-/>>,<l-/>>);(<L-o>,<l-o>);/
+ (<L.>,<l.>);(<L//>,<l//>);/
+ (<L<>,<l<>);(<L_>,<l_>);/
+ (<M>,<m>);(<M'>,<m'>);/
+ (<M-.>,<m-.>);(<M-o>,<m-o>);/
+ (<M.>,<m.>);(<N>,<n>);/
+ (<N'>,<n'>);(<N,>,<n,>);/
+ (<N-.>,<n-.>);(<N-/>>,<n-/>>);/
+ (<N-o>,<n-o>);(<N.>,<n.>);/
+ (<N<>,<n<>);(<N?>,<n?>);/
+ (<NG>,<ng>);(<N_>,<n_>);/
+ (<O>,<o>);(<O!>,<o!>);/
+ (<O!!>,<o!!>);(<O">,<o">);/
+ (<O'>,<o'>);(<O(>,<o(>);/
+ (<O)>,<o)>);(<O->,<o->);/
+ (<O-!>,<o-!>);(<O-'>,<o-'>);/
+ (<O-.>,<o-.>);(<O-o>,<o-o>);/
+ (<O//>,<o//>);(<O1>,<o1>);/
+ (<O2>,<o2>);(<O9>,<o9>);/
+ (<O9!>,<o9!>);(<O9'>,<o9'>);/
+ (<O9-.>,<o9-.>);(<O92>,<o92>);/
+ (<O9?>,<o9?>);(<O:>,<o:>);/
+ (<O;>,<o;>);(<O<>,<o<>);/
+ (<O/>>,<o/>>);(<O/>!>,<o/>!>);/
+ (<O/>'>,<o/>'>);/
+ (<O/>-.>,<o/>-.>);/
+ (<O/>2>,<o/>2>);/
+ (<O/>?>,<o/>?>);(<O?>,<o?>);/
+ (<O?'>,<o?'>);(<O?:>,<o?:>);/
+ (<OE>,<oe>);(<OI>,<oi>);/
+ (<P>,<p>);(<P'>,<p'>);/
+ (<P-o>,<p-o>);(<P.>,<p.>);/
+ (<Q>,<q>);(<Q-o>,<q-o>);/
+ (<R>,<r>);(<R!!>,<r!!>);/
+ (<R'>,<r'>);(<R)>,<r)>);/
+ (<R,>,<r,>);(<R--.>,<r--.>);/
+ (<R-.>,<r-.>);(<R-o>,<r-o>);/
+ (<R.>,<r.>);(<R<>,<r<>);/
+ (<R_>,<r_>);(<S>,<s>);/
+ (<S'>,<s'>);(<S'.>,<s'.>);/
+ (<S,>,<s,>);(<S-.>,<s-.>);/
+ (<S-o>,<s-o>);(<S.>,<s.>);/
+ (<S.-.>,<s.-.>);(<S<>,<s<>);/
+ (<S<.>,<s<.>);(<S/>>,<s/>>);/
+ (<T>,<t>);(<T,>,<t,>);/
+ (<T-.>,<t-.>);(<T-/>>,<t-/>>);/
+ (<T-o>,<t-o>);(<T.>,<t.>);/
+ (<T//>,<t//>);(<T<>,<t<>);/
+ (<TH>,<th>);(<T_>,<t_>);/
+ (<U>,<u>);(<U!>,<u!>);/
+ (<U!!>,<u!!>);(<U">,<u">);/
+ (<U'>,<u'>);(<U(>,<u(>);/
+ (<U)>,<u)>);(<U->,<u->);/
+ (<U--:>,<u--:>);(<U-.>,<u-.>);/
+ (<U-:>,<u-:>);(<U-/>>,<u-/>>);/
+ (<U-?>,<u-?>);(<U-o>,<u-o>);/
+ (<U0>,<u0>);(<U2>,<u2>);/
+ (<U9>,<u9>);(<U9!>,<u9!>);/
+ (<U9'>,<u9'>);(<U9-.>,<u9-.>);/
+ (<U92>,<u92>);(<U9?>,<u9?>);/
+ (<U:>,<u:>);(<U:!>,<u:!>);/
+ (<U:'>,<u:'>);(<U:->,<u:->);/
+ (<U:<>,<u:<>);(<U;>,<u;>);/
+ (<U<>,<u<>);(<U/>>,<u/>>);/
+ (<U?>,<u?>);(<U?'>,<u?'>);/
+ (<V>,<v>);(<V-.>,<v-.>);/
+ (<V-o>,<v-o>);(<V?>,<v?>);/
+ (<W>,<w>);(<W!>,<w!>);/
+ (<W'>,<w'>);(<W-.>,<w-.>);/
+ (<W-o>,<w-o>);(<W.>,<w.>);/
+ (<W:>,<w:>);(<W/>>,<w/>>);/
+ (<X>,<x>);(<X-o>,<x-o>);/
+ (<X.>,<x.>);(<X:>,<x:>);/
+ (<Y>,<y>);(<Y!>,<y!>);/
+ (<Y'>,<y'>);(<Y-.>,<y-.>);/
+ (<Y-o>,<y-o>);(<Y.>,<y.>);/
+ (<Y2>,<y2>);(<Y/>>,<y/>>);/
+ (<Y?>,<y?>);(<Z>,<z>);/
+ (<Z'>,<z'>);(<Z-.>,<z-.>);/
+ (<Z-o>,<z-o>);(<Z.>,<z.>);/
+ (<Z//>,<z//>);(<Z<>,<z<>);/
+ (<Z/>>,<z/>>);(<Z_>,<z_>);/
+ (<A%>,<a%>);(<A*>,<a*>);/
+ (<B*>,<b*>);(<C*>,<c*>);/
+ (<D*>,<d*>);(<E%>,<e%>);/
+ (<E*>,<e*>);(<F*>,<f*>);/
+ (<G*>,<g*>);(<H*>,<h*>);/
+ (<I%>,<i%>);(<I*>,<i*>);/
+ (<J*>,<j*>);(<K*>,<k*>);/
+ (<L*>,<l*>);(<M*>,<m*>);/
+ (<N*>,<n*>);(<O%>,<o%>);/
+ (<O*>,<o*>);(<P*>,<p*>);/
+ (<Q*>,<q*>);(<R*>,<r*>);/
+ (<S*>,<s*>);(<T*>,<t*>);/
+ (<U%>,<u%>);(<U*>,<u*>);/
+ (<V*>,<v*>);(<W%>,<w%>);/
+ (<W*>,<w*>);(<X*>,<x*>);/
+ (<Y%>,<y%>);(<Y*>,<y*>);/
+ (<Z*>,<z*>);(<%">,<%'>);/
+ (<=">,<='>);(<A=>,<a=>);/
+ (<B=>,<b=>);(<C%>,<c%>);/
+ (<C3>,<c3>);(<C=>,<c=>);/
+ (<D%>,<d%>);(<D=>,<d=>);/
+ (<DS>,<ds>);(<DZ>,<dz>);/
+ (<E=>,<e=>);(<F3>,<f3>);/
+ (<F=>,<f=>);(<G%>,<g%>);/
+ (<G3>,<g3>);(<G=>,<g=>);/
+ (<H=>,<h=>);(<I=>,<i=>);/
+ (<IE>,<ie>);(<II>,<ii>);/
+ (<IO>,<io>);(<J%>,<j%>);/
+ (<J=>,<j=>);(<JA>,<ja>);/
+ (<JE>,<je>);(<JU>,<ju>);/
+ (<K=>,<k=>);(<KJ>,<kj>);/
+ (<L=>,<l=>);(<LJ>,<lj>);/
+ (<M=>,<m=>);(<N=>,<n=>);/
+ (<NJ>,<nj>);(<O3>,<o3>);/
+ (<O=>,<o=>);(<P=>,<p=>);/
+ (<R=>,<r=>);(<S%>,<s%>);/
+ (<S=>,<s=>);(<Sc>,<sc>);/
+ (<T=>,<t=>);(<Ts>,<ts>);/
+ (<U=>,<u=>);(<V3>,<v3>);/
+ (<V=>,<v=>);(<Y3>,<y3>);/
+ (<Y=>,<y=>);(<YI>,<yi>);/
+ (<Z%>,<z%>);(<Z=>,<z=>)
+
+toupper (<a>,<A>);(<a!>,<A!>);/
+ (<a!!>,<A!!>);(<a'>,<A'>);/
+ (<a(>,<A(>);(<a(!>,<A(!>);/
+ (<a('>,<A('>);(<a(-.>,<A(-.>);/
+ (<a(2>,<A(2>);(<a(?>,<A(?>);/
+ (<a)>,<A)>);(<a->,<A->);/
+ (<a-.>,<A-.>);(<a-0>,<A-0>);/
+ (<a-o>,<A-o>);(<a1>,<A1>);/
+ (<a2>,<A2>);(<a3>,<A3>);/
+ (<a7>,<A7>);(<a:>,<A:>);/
+ (<a;>,<A;>);(<a<>,<A<>);/
+ (<a/>>,<A/>>);(<a/>!>,<A/>!>);/
+ (<a/>'>,<A/>'>);/
+ (<a/>-.>,<A/>-.>);/
+ (<a/>2>,<A/>2>);/
+ (<a/>?>,<A/>?>);(<a?>,<A?>);/
+ (<aa>,<AA>);(<aa'>,<AA'>);/
+ (<ae>,<AE>);(<ae'>,<AE'>);/
+ (<b>,<B>);(<b-.>,<B-.>);/
+ (<b-o>,<B-o>);(<b.>,<B.>);/
+ (<b_>,<B_>);(<c>,<C>);/
+ (<c'>,<C'>);(<c,>,<C,>);/
+ (<c,'>,<C,'>);(<c-o>,<C-o>);/
+ (<c.>,<C.>);(<c2>,<C2>);/
+ (<c<>,<C<>);(<c/>>,<C/>>);/
+ (<d>,<D>);(<d,>,<D,>);/
+ (<d->,<D->);(<d-.>,<D-.>);/
+ (<d-/>>,<D-/>>);(<d-o>,<D-o>);/
+ (<d.>,<D.>);(<d//>,<D//>);/
+ (<d<>,<D<>);(<d_>,<D_>);/
+ (<e>,<E>);(<e!>,<E!>);/
+ (<e!!>,<E!!>);(<e'>,<E'>);/
+ (<e(>,<E(>);(<e)>,<E)>);/
+ (<e,(>,<E,(>);(<e->,<E->);/
+ (<e-!>,<E-!>);(<e-'>,<E-'>);/
+ (<e-.>,<E-.>);(<e-/>>,<E-/>>);/
+ (<e-?>,<E-?>);(<e-o>,<E-o>);/
+ (<e.>,<E.>);(<e2>,<E2>);/
+ (<e:>,<E:>);(<e;>,<E;>);/
+ (<e<>,<E<>);(<e/>>,<E/>>);/
+ (<e/>!>,<E/>!>);/
+ (<e/>'>,<E/>'>);/
+ (<e/>-.>,<E/>-.>);/
+ (<e/>2>,<E/>2>);/
+ (<e/>?>,<E/>?>);(<e?>,<E?>);/
+ (<ed>,<ED>);(<ez>,<EZ>);/
+ (<f>,<F>);(<f-o>,<F-o>);/
+ (<f.>,<F.>);(<f2>,<F2>);/
+ (<g>,<G>);(<g'>,<G'>);/
+ (<g(>,<G(>);(<g,>,<G,>);/
+ (<g->,<G->);(<g-o>,<G-o>);/
+ (<g.>,<G.>);(<g//>,<G//>);/
+ (<g<>,<G<>);(<g/>>,<G/>>);/
+ (<h>,<H>);(<h,>,<H,>);/
+ (<h-(>,<H-(>);(<h-.>,<H-.>);/
+ (<h-o>,<H-o>);(<h.>,<H.>);/
+ (<h//>,<H//>);(<h:>,<H:>);/
+ (<h/>>,<H/>>);(<i>,<I>);/
+ (<i!>,<I!>);(<i!!>,<I!!>);/
+ (<i'>,<I'>);(<i(>,<I(>);/
+ (<i)>,<I)>);(<i->,<I->);/
+ (<i-.>,<I-.>);(<i-?>,<I-?>);/
+ (<i-o>,<I-o>);(<i.>,<I.>);/
+ (<i2>,<I2>);(<i:>,<I:>);/
+ (<i:'>,<I:'>);(<i;>,<I;>);/
+ (<i<>,<I<>);(<i/>>,<I/>>);/
+ (<i?>,<I?>);(<ij>,<IJ>);/
+ (<j>,<J>);(<j-o>,<J-o>);/
+ (<j/>>,<J/>>);(<k>,<K>);/
+ (<k'>,<K'>);(<k,>,<K,>);/
+ (<k-.>,<K-.>);(<k-o>,<K-o>);/
+ (<k2>,<K2>);(<k<>,<K<>);/
+ (<k_>,<K_>);(<l>,<L>);/
+ (<l'>,<L'>);(<l,>,<L,>);/
+ (<l--.>,<L--.>);(<l-.>,<L-.>);/
+ (<l-/>>,<L-/>>);(<l-o>,<L-o>);/
+ (<l.>,<L.>);(<l//>,<L//>);/
+ (<l<>,<L<>);(<l_>,<L_>);/
+ (<m>,<M>);(<m'>,<M'>);/
+ (<m-.>,<M-.>);(<m-o>,<M-o>);/
+ (<m.>,<M.>);(<n>,<N>);/
+ (<n'>,<N'>);(<n,>,<N,>);/
+ (<n-.>,<N-.>);(<n-/>>,<N-/>>);/
+ (<n-o>,<N-o>);(<n.>,<N.>);/
+ (<n<>,<N<>);(<n?>,<N?>);/
+ (<ng>,<NG>);(<n_>,<N_>);/
+ (<o>,<O>);(<o!>,<O!>);/
+ (<o!!>,<O!!>);(<o">,<O">);/
+ (<o'>,<O'>);(<o(>,<O(>);/
+ (<o)>,<O)>);(<o->,<O->);/
+ (<o-!>,<O-!>);(<o-'>,<O-'>);/
+ (<o-.>,<O-.>);(<o-o>,<O-o>);/
+ (<o//>,<O//>);(<o1>,<O1>);/
+ (<o2>,<O2>);(<o9>,<O9>);/
+ (<o9!>,<O9!>);(<o9'>,<O9'>);/
+ (<o9-.>,<O9-.>);(<o92>,<O92>);/
+ (<o9?>,<O9?>);(<o:>,<O:>);/
+ (<o;>,<O;>);(<o<>,<O<>);/
+ (<o/>>,<O/>>);(<o/>!>,<O/>!>);/
+ (<o/>'>,<O/>'>);/
+ (<o/>-.>,<O/>-.>);/
+ (<o/>2>,<O/>2>);/
+ (<o/>?>,<O/>?>);(<o?>,<O?>);/
+ (<o?'>,<O?'>);(<o?:>,<O?:>);/
+ (<oe>,<OE>);(<oi>,<OI>);/
+ (<p>,<P>);(<p'>,<P'>);/
+ (<p-o>,<P-o>);(<p.>,<P.>);/
+ (<q>,<Q>);(<q-o>,<Q-o>);/
+ (<r>,<R>);(<r!!>,<R!!>);/
+ (<r'>,<R'>);(<r)>,<R)>);/
+ (<r,>,<R,>);(<r--.>,<R--.>);/
+ (<r-.>,<R-.>);(<r-o>,<R-o>);/
+ (<r.>,<R.>);(<r<>,<R<>);/
+ (<r_>,<R_>);(<s>,<S>);/
+ (<s'>,<S'>);(<s'.>,<S'.>);/
+ (<s,>,<S,>);(<s-.>,<S-.>);/
+ (<s-o>,<S-o>);(<s.>,<S.>);/
+ (<s.-.>,<S.-.>);(<s<>,<S<>);/
+ (<s<.>,<S<.>);(<s/>>,<S/>>);/
+ (<t>,<T>);(<t,>,<T,>);/
+ (<t-.>,<T-.>);(<t-/>>,<T-/>>);/
+ (<t-o>,<T-o>);(<t.>,<T.>);/
+ (<t//>,<T//>);(<t<>,<T<>);/
+ (<th>,<TH>);(<t_>,<T_>);/
+ (<u>,<U>);(<u!>,<U!>);/
+ (<u!!>,<U!!>);(<u">,<U">);/
+ (<u'>,<U'>);(<u(>,<U(>);/
+ (<u)>,<U)>);(<u->,<U->);/
+ (<u--:>,<U--:>);(<u-.>,<U-.>);/
+ (<u-:>,<U-:>);(<u-/>>,<U-/>>);/
+ (<u-?>,<U-?>);(<u-o>,<U-o>);/
+ (<u0>,<U0>);(<u2>,<U2>);/
+ (<u9>,<U9>);(<u9!>,<U9!>);/
+ (<u9'>,<U9'>);(<u9-.>,<U9-.>);/
+ (<u92>,<U92>);(<u9?>,<U9?>);/
+ (<u:>,<U:>);(<u:!>,<U:!>);/
+ (<u:'>,<U:'>);(<u:->,<U:->);/
+ (<u:<>,<U:<>);(<u;>,<U;>);/
+ (<u<>,<U<>);(<u/>>,<U/>>);/
+ (<u?>,<U?>);(<u?'>,<U?'>);/
+ (<v>,<V>);(<v-.>,<V-.>);/
+ (<v-o>,<V-o>);(<v?>,<V?>);/
+ (<w>,<W>);(<w!>,<W!>);/
+ (<w'>,<W'>);(<w-.>,<W-.>);/
+ (<w-o>,<W-o>);(<w.>,<W.>);/
+ (<w:>,<W:>);(<w/>>,<W/>>);/
+ (<x>,<X>);(<x-o>,<X-o>);/
+ (<x.>,<X.>);(<x:>,<X:>);/
+ (<y>,<Y>);(<y!>,<Y!>);/
+ (<y'>,<Y'>);(<y-.>,<Y-.>);/
+ (<y-o>,<Y-o>);(<y.>,<Y.>);/
+ (<y2>,<Y2>);(<y/>>,<Y/>>);/
+ (<y?>,<Y?>);(<z>,<Z>);/
+ (<z'>,<Z'>);(<z-.>,<Z-.>);/
+ (<z-o>,<Z-o>);(<z.>,<Z.>);/
+ (<z//>,<Z//>);(<z<>,<Z<>);/
+ (<z/>>,<Z/>>);(<z_>,<Z_>);/
+ (<a%>,<A%>);(<a*>,<A*>);/
+ (<b*>,<B*>);(<c*>,<C*>);/
+ (<d*>,<D*>);(<e%>,<E%>);/
+ (<e*>,<E*>);(<f*>,<F*>);/
+ (<g*>,<G*>);(<h*>,<H*>);/
+ (<i%>,<I%>);(<i*>,<I*>);/
+ (<j*>,<J*>);(<k*>,<K*>);/
+ (<l*>,<L*>);(<m*>,<M*>);/
+ (<n*>,<N*>);(<o%>,<O%>);/
+ (<o*>,<O*>);(<p*>,<P*>);/
+ (<q*>,<Q*>);(<r*>,<R*>);/
+ (<s*>,<S*>);(<t*>,<T*>);/
+ (<u%>,<U%>);(<u*>,<U*>);/
+ (<v*>,<V*>);(<w%>,<W%>);/
+ (<w*>,<W*>);(<x*>,<X*>);/
+ (<y%>,<Y%>);(<y*>,<Y*>);/
+ (<z*>,<Z*>);(<%'>,<%">);/
+ (<='>,<=">);(<a=>,<A=>);/
+ (<b=>,<B=>);(<c%>,<C%>);/
+ (<c3>,<C3>);(<c=>,<C=>);/
+ (<d%>,<D%>);(<d=>,<D=>);/
+ (<ds>,<DS>);(<dz>,<DZ>);/
+ (<e=>,<E=>);(<f3>,<F3>);/
+ (<f=>,<F=>);(<g%>,<G%>);/
+ (<g3>,<G3>);(<g=>,<G=>);/
+ (<h=>,<H=>);(<i=>,<I=>);/
+ (<ie>,<IE>);(<ii>,<II>);/
+ (<io>,<IO>);(<j%>,<J%>);/
+ (<j=>,<J=>);(<ja>,<JA>);/
+ (<je>,<JE>);(<ju>,<JU>);/
+ (<k=>,<K=>);(<kj>,<KJ>);/
+ (<l=>,<L=>);(<lj>,<LJ>);/
+ (<m=>,<M=>);(<n=>,<N=>);/
+ (<nj>,<NJ>);(<o3>,<O3>);/
+ (<o=>,<O=>);(<p=>,<P=>);/
+ (<r=>,<R=>);(<s%>,<S%>);/
+ (<s=>,<S=>);(<sc>,<Sc>);/
+ (<t=>,<T=>);(<ts>,<Ts>);/
+ (<u=>,<U=>);(<v3>,<V3>);/
+ (<v=>,<V=>);(<y3>,<Y3>);/
+ (<y=>,<Y=>);(<yi>,<YI>);/
+ (<z%>,<Z%>);(<z=>,<Z=>);/
+ (<*s>,<S*>)
+
+END LC_CTYPE
+
+LC_MONETARY
+int_curr_symbol "<D><K><K><SP>"
+currency_symbol "<Cu>"
+mon_decimal_point "<,>"
+mon_thousands_sep "<.>"
+mon_grouping 3;3
+positive_sign ""
+negative_sign "<->"
+int_frac_digits 2
+frac_digits 2
+p_cs_precedes 1
+p_sep_by_space 0
+n_cs_precedes 1
+n_sep_by_space 0
+p_sign_posn 1
+n_sign_posn 1
+END LC_MONETARY
+
+LC_NUMERIC
+decimal_point "<,>"
+thousands_sep "<.>"
+grouping 3;3
+END LC_NUMERIC
+
+LC_TIME
+abday "<S><u><n>";"<M><o><n>";/
+ "<T><u><e>";"<W><e><d>";/
+ "<T><h><u>";"<F><r><i>";/
+ "<S><a><t>"
+day "<S><u><n><d><a><y>";/
+ "<M><o><n><d><a><y>";/
+ "<T><u><e><s><d><a><y>";/
+ "<W><e><d><n><e><s><d><a><y>";/
+ "<T><h><u><r><s><d><a><y>";/
+ "<F><r><i><d><a><y>";/
+ "<S><a><t><u><r><d><a><y>"
+abmon "<J><a><n>";"<F><e><b>";/
+ "<M><a><r>";"<A><p><r>";/
+ "<M><a><y>";"<J><u><n>";/
+ "<J><u><l>";"<A><u><g>";/
+ "<S><e><p>";"<O><c><t>";/
+ "<N><o><v>";"<D><e><c>"
+mon "<J><a><n><u><a><r><y>";/
+ "<F><e><b><r><u><a><r><y>";/
+ "<M><a><r><c><h>";/
+ "<A><p><r><i><l>";/
+ "<M><a><y>";/
+ "<J><u><n><e>";/
+ "<J><u><l><y>";/
+ "<A><u><g><u><s><t>";/
+ "<S><e><p><t><e><m><b><e><r>";/
+ "<O><c><t><o><b><e><r>";/
+ "<N><o><v><e><m><b><e><r>";/
+ "<D><e><c><e><m><b><e><r>"
+% date formats following ISO 8601-1988
+d_t_fmt "<%><Y><-><%><m><-><%><d><T><%><T><SP><%><Z>"
+d_fmt "<%><Y><-><%><m><-><%><d>"
+t_fmt "<%><T>"
+am_pm "";""
+t_fmt_ampm ""
+END LC_TIME
+
+LC_MESSAGES
+yesexpr "<<(><1><J><j><s><S><y><Y><o><O><)/>><.><*>"
+noexpr "<<(><0><n><N><)/>><.><*>"
+END LC_MESSAGES
diff --git a/usr.bin/colldef/parse.y b/usr.bin/colldef/parse.y
new file mode 100644
index 0000000..e316e1c
--- /dev/null
+++ b/usr.bin/colldef/parse.y
@@ -0,0 +1,289 @@
+%{
+/*-
+ * 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.9 1997/06/26 11:25:17 charnier Exp $
+ */
+
+#include <err.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sysexits.h>
+#include "collate.h"
+
+extern int line_no;
+extern FILE *yyin;
+void yyerror(char *fmt, ...);
+static void usage __P((void));
+
+char map_name[FILENAME_MAX] = ".";
+
+char __collate_version[STR_LEN];
+u_char 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_chain_pri __collate_chain_pri_table[TABLE_SIZE];
+int 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> CHAIN
+%token <str> DEFN
+%token <ch> CHAR
+%%
+collate : statment_list
+;
+statment_list : statment
+ | statment_list '\n' statment
+;
+statment :
+ | charmap
+ | substitute
+ | order
+;
+charmap : DEFN CHAR {
+ strcpy(charmap_table[$2], $1);
+}
+;
+substitute : SUBSTITUTE STRING WITH STRING {
+ strcpy(__collate_substitute_table[$2[0]], $4);
+}
+;
+order : ORDER order_list {
+ FILE *fp;
+ int ch;
+
+ for (ch = 0; ch < UCHAR_MAX + 1; ch++)
+ if (!__collate_char_pri_table[ch].prim)
+ yyerror("Char 0x%02x not present", ch);
+
+ fp = fopen(out_file, "w");
+ if(!fp)
+ err(EX_UNAVAILABLE, "can't open destination file %s",
+ out_file);
+
+ strcpy(__collate_version, COLLATE_VERSION);
+ fwrite(__collate_version, sizeof(__collate_version), 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);
+ if (fflush(fp))
+ err(EX_UNAVAILABLE, "IO error writting to destination file %s",
+ out_file);
+ fclose(fp);
+#ifdef COLLATE_DEBUG
+ if (debug)
+ collate_print_tables();
+#endif
+ exit(EX_OK);
+}
+;
+order_list : item
+ | order_list ';' item
+;
+item : CHAR {
+ if (__collate_char_pri_table[$1].prim)
+ yyerror("Char 0x%02x duplicated", $1);
+ __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++;
+}
+ | CHAR RANGE CHAR {
+ u_int i;
+
+ if ($3 <= $1)
+ yyerror("Illegal range 0x%02x -- 0x%02x", $1, $3);
+
+ for (i = $1; i <= $3; i++) {
+ if (__collate_char_pri_table[(u_char)i].prim)
+ yyerror("Char 0x%02x duplicated", (u_char)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 {
+ if (__collate_char_pri_table[$1].prim)
+ yyerror("Char 0x%02x duplicated", $1);
+ __collate_char_pri_table[$1].prim = prim_pri;
+}
+ | CHAR RANGE CHAR {
+ u_int i;
+
+ if ($3 <= $1)
+ yyerror("Illegal range 0x%02x -- 0x%02x",
+ $1, $3);
+
+ for (i = $1; i <= $3; i++) {
+ if (__collate_char_pri_table[(u_char)i].prim)
+ yyerror("Char 0x%02x duplicated", (u_char)i);
+ __collate_char_pri_table[(u_char)i].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 {
+ if (__collate_char_pri_table[$1].prim)
+ yyerror("Char 0x%02x duplicated", $1);
+ __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 0x%02x -- 0x%02x",
+ $1, $3);
+
+ for (i = $1; i <= $3; i++) {
+ if (__collate_char_pri_table[(u_char)i].prim)
+ yyerror("Char 0x%02x duplicated", (u_char)i);
+ __collate_char_pri_table[(u_char)i].prim = prim_pri;
+ __collate_char_pri_table[(u_char)i].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:I:")) != EOF) {
+#else
+ while((ch = getopt(ac, av, ":o:I:")) != EOF) {
+#endif
+ switch (ch)
+ {
+#ifdef COLLATE_DEBUG
+ case 'd':
+ debug++;
+ break;
+#endif
+ case 'o':
+ out_file = optarg;
+ break;
+
+ case 'I':
+ strcpy(map_name, optarg);
+ break;
+
+ default:
+ usage();
+ }
+ }
+ 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;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: colldef [-o out_file] [-I map_dir] [filename]\n");
+ exit(EX_USAGE);
+}
+
+void yyerror(char *fmt, ...)
+{
+ va_list ap;
+ char msg[128];
+
+ va_start(ap, fmt);
+ vsprintf(msg, fmt, ap);
+ va_end(ap);
+ errx(EX_UNAVAILABLE, "%s near line %d", msg, line_no);
+}
+
+#ifdef COLLATE_DEBUG
+collate_print_tables()
+{
+ int i;
+ struct __collate_st_chain_pri *p2;
+
+ printf("Substitute table:\n");
+ for (i = 0; i < UCHAR_MAX + 1; i++)
+ if (i != *__collate_substitute_table[i])
+ printf("\t'%c' --> \"%s\"\n", i,
+ __collate_substitute_table[i]);
+ printf("Chain priority table:\n");
+ for (p2 = __collate_chain_pri_table; p2->str[0]; p2++)
+ printf("\t\"%s\" : %d %d\n\n", p2->str, p2->prim, p2->sec);
+ printf("Char priority table:\n");
+ for (i = 0; i < UCHAR_MAX + 1; i++)
+ printf("\t'%c' : %d %d\n", i, __collate_char_pri_table[i].prim,
+ __collate_char_pri_table[i].sec);
+}
+#endif
diff --git a/usr.bin/colldef/scan.l b/usr.bin/colldef/scan.l
new file mode 100644
index 0000000..3e7dd6b
--- /dev/null
+++ b/usr.bin/colldef/scan.l
@@ -0,0 +1,293 @@
+%x string name charmap defn nchar subs
+%{
+/*-
+ * 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$
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <unistd.h>
+#include <string.h>
+#include <sysexits.h>
+#include "collate.h"
+#include "y.tab.h"
+
+int line_no = 1, save_no;
+u_char buf[STR_LEN], *ptr;
+FILE *map_fp;
+extern char map_name[];
+extern u_char charmap_table[UCHAR_MAX + 1][STR_LEN];
+YY_BUFFER_STATE main_buf, map_buf;
+#ifdef FLEX_DEBUG
+YYSTYPE yylval;
+#endif /* FLEX_DEBUG */
+%}
+%%
+<INITIAL,charmap,nchar,subs>[ \t]+ ;
+<subs>\" { ptr = buf; BEGIN(string); }
+<INITIAL>\< { ptr = buf; BEGIN(name); }
+^#.*\n line_no++;
+^\n line_no++;
+<INITIAL>\\\n line_no++;
+<INITIAL,nchar>\\t { yylval.ch = '\t'; return CHAR; }
+<INITIAL,nchar>\\n { yylval.ch = '\n'; return CHAR; }
+<INITIAL,nchar>\\b { yylval.ch = '\b'; return CHAR; }
+<INITIAL,nchar>\\f { yylval.ch = '\f'; return CHAR; }
+<INITIAL,nchar>\\v { yylval.ch = '\v'; return CHAR; }
+<INITIAL,nchar>\\r { yylval.ch = '\r'; return CHAR; }
+<INITIAL,nchar>\\a { yylval.ch = '\a'; return CHAR; }
+<INITIAL,nchar>\\. { yylval.ch = yytext[1]; return CHAR; }
+<subs>\n {
+ line_no++;
+ BEGIN(INITIAL);
+ return '\n';
+}
+<INITIAL,nchar>\n {
+ line_no++;
+ if (map_fp != NULL) {
+ ptr = buf;
+ BEGIN(defn);
+ }
+ return '\n';
+}
+<INITIAL>[;,{}()] return *yytext;
+<INITIAL>substitute { BEGIN(subs); return SUBSTITUTE; }
+<subs>with return WITH;
+<INITIAL>order return ORDER;
+<INITIAL>charmap BEGIN(charmap);
+<INITIAL>;[ \t]*\.\.\.[ \t]*; return RANGE;
+<INITIAL,nchar>\\[0-7]{3} {
+ u_int v;
+
+ sscanf(&yytext[1], "%o", &v);
+ yylval.ch = (u_char)v;
+ return CHAR;
+}
+<INITIAL,nchar>\\x[0-9a-z]{2} {
+ u_int v;
+
+ sscanf(&yytext[2], "%x", &v);
+ yylval.ch = (u_char)v;
+ return CHAR;
+}
+<INITIAL>[^;,{}() \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;
+}
+<nchar>. {
+ yylval.ch = *yytext;
+ return CHAR;
+}
+<defn>[ \t]+ {
+ if (ptr == buf)
+ errx(EX_UNAVAILABLE, "map expected near line %u of %s",
+ line_no, map_name);
+ *ptr = '\0';
+ strcpy(yylval.str, buf);
+ BEGIN(nchar);
+ return DEFN;
+}
+<name>\/\/ {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "name buffer overflaw near line %u, character '/'",
+ line_no);
+ *ptr++ = '/';
+}
+<name>\/\> {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "name buffer overflaw near line %u, character '>'",
+ line_no);
+ *ptr++ = '>';
+}
+<string>\\\" {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "string buffer overflaw near line %u, character '\"'",
+ line_no);
+ *ptr++ = '"';
+}
+<name>\> {
+ u_int i;
+
+ if (ptr == buf)
+ errx(EX_UNAVAILABLE, "non-empty name expected near line %u",
+ line_no);
+ *ptr = '\0';
+ for (i = 0; i <= UCHAR_MAX; i++)
+ if (strcmp(charmap_table[i], buf) == 0)
+ goto findit;
+ errx(EX_UNAVAILABLE, "name <%s> not 'charmap'-defined near line %u",
+ buf, line_no);
+ findit:
+ yylval.ch = i;
+ BEGIN(INITIAL);
+ return CHAR;
+}
+<string>\" {
+ *ptr = '\0';
+ strcpy(yylval.str, buf);
+ BEGIN(subs);
+ return STRING;
+}
+<name,defn>. {
+ char *s = (map_fp != NULL) ? map_name : "input";
+
+ if (!isascii(*yytext) || !isprint(*yytext))
+ errx(EX_UNAVAILABLE, "non-ASCII or non-printable character 0x%02x not allowed in the map/name near line %u of %s",
+ *yytext, line_no, s);
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "map/name buffer overflaw near line %u of %s, character '%c'",
+ line_no, s, *yytext);
+ *ptr++ = *yytext;
+}
+<string>\\t {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "string buffer overflaw near line %u, character '\\t'",
+ line_no);
+ *ptr++ = '\t';
+}
+<string>\\b {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "string buffer overflaw near line %u, character '\\b'",
+ line_no);
+ *ptr++ = '\b';
+}
+<string>\\f {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "string buffer overflaw near line %u, character '\\f'",
+ line_no);
+ *ptr++ = '\f';
+}
+<string>\\v {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "string buffer overflaw near line %u, character '\\v'",
+ line_no);
+ *ptr++ = '\v';
+}
+<string>\\n {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "string buffer overflaw near line %u, character '\\n'",
+ line_no);
+ *ptr++ = '\n';
+}
+<string>\\r {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "string buffer overflaw near line %u, character '\\r'",
+ line_no);
+ *ptr++ = '\r';
+}
+<string>\\a {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "string buffer overflaw near line %u, character '\\a'",
+ line_no);
+ *ptr++ = '\a';
+}
+<name,string,defn>\n {
+ char *s = (map_fp != NULL) ? map_name : "input";
+
+ errx(EX_UNAVAILABLE, "unterminated map/name/string near line %u of %s", line_no, s);
+}
+<name,string,nchar><<EOF>> {
+ char *s = (map_fp != NULL) ? map_name : "input";
+
+ errx(EX_UNAVAILABLE, "premature EOF in the name/string/char near line %u of %s", line_no, s);
+}
+<string>\\x[0-9a-f]{2} {
+ u_int v;
+
+ sscanf(&yytext[2], "%x", &v);
+ *ptr++ = (u_char)v;
+}
+<string>\\[0-7]{3} {
+ u_int v;
+
+ sscanf(&yytext[1], "%o", &v);
+ *ptr++ = (u_char)v;
+}
+<string>\\. {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "string buffer overflaw near line %u, character '%c'",
+ line_no, yytext[1]);
+ *ptr++ = yytext[1];
+}
+<string>. {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "string buffer overflaw near line %u, character '%c'",
+ line_no, *yytext);
+ *ptr++ = *yytext;
+}
+<charmap>[^ \t\n]+ {
+ strcat(map_name, "/");
+ strcat(map_name, yytext);
+ if((map_fp = fopen(map_name, "r")) == NULL)
+ err(EX_UNAVAILABLE, "can't open 'charmap' file %s",
+ map_name);
+ save_no = line_no;
+ line_no = 1;
+ map_buf = yy_new_buffer(map_fp, YY_BUF_SIZE);
+ main_buf = YY_CURRENT_BUFFER;
+ yy_switch_to_buffer(map_buf);
+ ptr = buf;
+ BEGIN(defn);
+}
+<charmap>\n {
+ errx(EX_UNAVAILABLE, "'charmap' file name expected near line %u",
+ line_no);
+}
+<charmap><<EOF>> {
+ errx(EX_UNAVAILABLE, "'charmap' file name expected near line %u",
+ line_no);
+}
+<INITIAL,defn><<EOF>> {
+ if(map_fp != NULL) {
+ if (ptr != buf)
+ errx(EX_UNAVAILABLE, "premature EOF in the map near line %u of %s", line_no, map_name);
+ yy_switch_to_buffer(main_buf);
+ yy_delete_buffer(map_buf);
+ fclose(map_fp);
+ map_fp = NULL;
+ line_no = save_no;
+ BEGIN(INITIAL);
+ } 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..1a50405
--- /dev/null
+++ b/usr.bin/colrm/colrm.c
@@ -0,0 +1,141 @@
+/*-
+ * 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.
+ *
+ * $Id: colrm.c,v 1.3 1997/06/26 11:26:20 charnier Exp $
+ */
+
+#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.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define TAB 8
+
+void check __P((FILE *));
+static 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, "")) != -1)
+ 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)
+ errx(1, "illegal column -- %s", argv[1]);
+ /* FALLTHROUGH */
+ case 1:
+ start = strtol(argv[0], &p, 10);
+ if (start <= 0 || *p)
+ errx(1, "illegal column -- %s", argv[0]);
+ break;
+ case 0:
+ break;
+ default:
+ usage();
+ }
+
+ if (stop && start > stop)
+ errx(1, "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(1, "%s", stream == stdin ? "stdin" : "stdout");
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: colrm [start [stop]]\n");
+ exit(1);
+}
+
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..cf8c7a9
--- /dev/null
+++ b/usr.bin/column/column.c
@@ -0,0 +1,305 @@
+/*
+ * 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.4 (Berkeley) 5/4/95";
+#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>
+#include <unistd.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")) != -1)
+ 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] [-s sep] [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..72ee36f
--- /dev/null
+++ b/usr.bin/comm/comm.1
@@ -0,0 +1,95 @@
+.\" 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.
+.Sh DIAGNOSTICS
+.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
+utility conforms to
+.St -p1003.2-92 .
diff --git a/usr.bin/comm/comm.c b/usr.bin/comm/comm.c
new file mode 100644
index 0000000..46707bb
--- /dev/null
+++ b/usr.bin/comm/comm.c
@@ -0,0 +1,185 @@
+/*
+ * 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.4 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAXLINELEN (LINE_MAX + 1)
+
+char *tabs[] = { "", "\t", "\t\t" };
+
+FILE *file __P((char *));
+void show __P((FILE *, char *, char *));
+static 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")) != -1)
+ 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) {
+ err(1, "%s", name);
+ }
+ return (fp);
+}
+
+static 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..f75572a
--- /dev/null
+++ b/usr.bin/compile_et/Makefile
@@ -0,0 +1,17 @@
+# $Id$
+
+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..e5cef6a
--- /dev/null
+++ b/usr.bin/compile_et/compile_et.1
@@ -0,0 +1,78 @@
+.\" Copyright (c) 1988 Massachusetts Institute of Technology,
+.\" Student Information Processing Board. All rights reserved.
+.\"
+.\" $Header: /home/ncvs/src/usr.bin/compile_et/compile_et.1,v 1.1 1995/01/14 22:29:30 wollman Exp $
+.\"
+.Dd November 22, 1988
+.Os
+.Dt COMPILE_ET 1
+.Sh NAME
+.Nm compile_et
+.Nd error table compiler
+.Sh SYNOPSIS
+.Nm compile_et
+.Ar file
+.Sh DESCRIPTION
+.Nm Compile_et
+converts a table listing error-code names and associated messages into
+a C source file suitable for use with the
+.Xr com_err 3
+library.
+.Pp
+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:
+
+.Em error_table name
+
+followed by up to 256 entries of the form:
+
+.Em error_code name ,
+"
+.Em string
+"
+
+and a final
+
+.Em end
+
+to indicate the end of the table.
+.Pp
+The name of the table is used to construct the name of a subroutine
+.Em initialize_XXXX_error_table
+which must be called in order for the
+.Xr com_err 3
+library to recognize the error table.
+.Pp
+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.
+.Pp
+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.
+.Pp
+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''.
+.Pp
+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
+.Nm compile_et
+uses a very simple parser based on
+.Xr yacc 1 ,
+its error recovery leaves much to be desired.
+.Sh "SEE ALSO"
+.Xr yacc 1 ,
+.Xr com_err 3
+.Pp
+.Rs
+.%A Ken Raeburn
+.%T "A Common Error Description Library for UNIX"
+.Re
diff --git a/usr.bin/compile_et/compile_et.c b/usr.bin/compile_et/compile_et.c
new file mode 100644
index 0000000..055a9b6
--- /dev/null
+++ b/usr.bin/compile_et/compile_et.c
@@ -0,0 +1,276 @@
+/*
+ *
+ * Copyright 1986, 1987, 1988
+ * by MIT Student Information Processing Board.
+ *
+ * For copyright info, see "mit-sipb-copyright.h".
+ *
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.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: /home/ncvs/src/usr.bin/compile_et/compile_et.c,v 1.3 1996/07/12 19:05:17 jkh 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;
+
+/* lex stuff */
+extern FILE *yyin;
+extern int yylineno;
+
+char * xmalloc (size) unsigned int size; {
+ char * p = malloc (size);
+ if (!p)
+ err(1, NULL);
+ 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, "usage: compile_et ERROR_TABLE\n");
+ exit (1);
+}
+
+static void dup_err (type, one, two) char const *type, *one, *two; {
+ warnx("multiple %s specified: `%s' and `%s'", 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;
+ 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 ("languages", 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 {
+ errx(1, "unknown language name `%s'\n\tpick one of: C K&R-C", arg);
+ }
+ }
+ else {
+ warnx("unknown control argument -`%s'", arg);
+ usage ();
+ }
+ }
+ }
+ if (!filename)
+ usage ();
+ if (!got_language)
+ language = lang_KRC;
+ else if (language == lang_CPP) {
+ errx(1, "sorry, C++ support is not yet finished");
+ }
+
+ 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, %dL, %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 (%dL)\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);
+ return 0;
+}
+
diff --git a/usr.bin/compile_et/compiler.h b/usr.bin/compile_et/compiler.h
new file mode 100644
index 0000000..3bf568d
--- /dev/null
+++ b/usr.bin/compile_et/compiler.h
@@ -0,0 +1,19 @@
+/*
+ * 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;
diff --git a/usr.bin/compile_et/error_table.y b/usr.bin/compile_et/error_table.y
new file mode 100644
index 0000000..c29db13
--- /dev/null
+++ b/usr.bin/compile_et/error_table.y
@@ -0,0 +1,237 @@
+%{
+#include <stdio.h>
+#include <stdlib.h>
+char *str_concat(), *ds(), *quote();
+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.3 1995/03/15 19:05:28 wpaul 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;
+
+void
+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;
+}
+
+void
+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;
+}
+
+void
+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);
+}
+
+void
+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..48d1b89
--- /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: /home/ncvs/src/usr.bin/compile_et/et_lex.lex.l,v 1.2 1995/01/14 22:29:33 wollman Exp $";
+#endif
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/compress/Makefile b/usr.bin/compress/Makefile
new file mode 100644
index 0000000..0f6f5dd
--- /dev/null
+++ b/usr.bin/compress/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.2 (Berkeley) 4/17/94
+
+PROG= compress
+SRCS= compress.c zopen.c
+LINKS= ${BINDIR}/compress ${BINDIR}/uncompress
+MLINKS= compress.1 uncompress.1
+
+# XXX zopen is not part of libc
+# MAN3=zopen.3
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/compress/compress.1 b/usr.bin/compress/compress.1
new file mode 100644
index 0000000..c800269
--- /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
+.Nm 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..32d9496
--- /dev/null
+++ b/usr.bin/compress/compress.c
@@ -0,0 +1,446 @@
+/*-
+ * 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
+
+#include "zopen.h"
+
+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")) != -1)
+ 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/zopen.3 b/usr.bin/compress/zopen.3
new file mode 100644
index 0000000..3d70f25
--- /dev/null
+++ b/usr.bin/compress/zopen.3
@@ -0,0 +1,140 @@
+.\" 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 "zopen.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
+.Bx 4.4 .
+.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..5e5357b
--- /dev/null
+++ b/usr.bin/compress/zopen.c
@@ -0,0 +1,741 @@
+/*-
+ * 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>
+#include "zopen.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/compress/zopen.h b/usr.bin/compress/zopen.h
new file mode 100644
index 0000000..322fd2d
--- /dev/null
+++ b/usr.bin/compress/zopen.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 1996
+ * FreeBSD 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY FreeBSD Inc. AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL [your name] OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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$
+ */
+
+#ifndef _ZOPEN_H_
+#define _ZOPEN_H_
+
+FILE *zopen __P((const char *, const char *, int));
+
+#endif /* _ZOPEN_H_ */
diff --git a/usr.bin/cpp/Makefile b/usr.bin/cpp/Makefile
new file mode 100644
index 0000000..95cf0b1
--- /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}${BINDIR}/cpp
+.else
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/cpp.sh ${DESTDIR}${BINDIR}/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..df09d6d
--- /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$
+#
+# 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..725dfb5
--- /dev/null
+++ b/usr.bin/ctags/ctags.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 1987, 1993, 1994, 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 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, 1995\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ctags.c 8.4 (Berkeley) 2/7/95";
+#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 *));
+static void usage __P((void));
+
+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")) != -1)
+ 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:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+ if (!argc)
+ usage();
+
+ 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);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: ctags [-BFadtuwvx] [-f tagsfile] file ...\n");
+ exit(1);
+}
+
+/*
+ * 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..3ba43ed
--- /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 delim
+.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 delim
+Use the first character of
+.Ar delim
+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..cc57685
--- /dev/null
+++ b/usr.bin/cut/cut.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
+ * 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.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int cflag;
+char dchar;
+int dflag;
+int fflag;
+int sflag;
+
+void c_cut __P((FILE *, char *));
+void f_cut __P((FILE *, char *));
+void get_list __P((char *));
+static 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")) != -1)
+ 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(1, "%s", *argv);
+ 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)
+ errx(1, "[-cf] list: illegal list value");
+ if (!stop || !start)
+ errx(1, "[-cf] list: values may not include zero");
+ if (stop > _POSIX2_LINE_MAX)
+ errx(1, "[-cf] list: %d too large (max %d)",
+ 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))
+ errx(1, "%s: line too long", 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');
+ }
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "%s\n%s\n",
+ "usage: cut -c list [file1 ...]",
+ " cut -f list [-s] [-d delim] [file ...]");
+ exit(1);
+}
diff --git a/usr.bin/dig/Makefile b/usr.bin/dig/Makefile
new file mode 100644
index 0000000..4ff062e
--- /dev/null
+++ b/usr.bin/dig/Makefile
@@ -0,0 +1,12 @@
+# $Id$
+
+.include "${.CURDIR}/../../usr.sbin/named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/tools
+.PATH: ${BIND_DIR}/tools/nslookup
+.PATH: ${BIND_DIR}/man
+
+PROG= dig
+SRCS= dig.c list.c subr.c debug.c send.c
+
+.include <bsd.prog.mk>
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..c6ca326
--- /dev/null
+++ b/usr.bin/dirname/dirname.c
@@ -0,0 +1,145 @@
+/*-
+ * 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.4 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *p;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ 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/dnsquery/Makefile b/usr.bin/dnsquery/Makefile
new file mode 100644
index 0000000..1be409a
--- /dev/null
+++ b/usr.bin/dnsquery/Makefile
@@ -0,0 +1,10 @@
+# $Id$
+
+.include "${.CURDIR}/../../usr.sbin/named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/tools
+.PATH: ${BIND_DIR}/man
+
+PROG= dnsquery
+
+.include <bsd.prog.mk>
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..e84a710
--- /dev/null
+++ b/usr.bin/du/du.1
@@ -0,0 +1,135 @@
+.\" 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
+.\" $Id: du.1,v 1.8 1997/02/22 19:54:52 peter Exp $
+.\"
+.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 | s | d Ar depth
+.Op Fl k
+.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.
+If the
+.Fl k
+flag is specified, the number displayed is the number of 1024-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 d Ar depth
+Displays all directories only
+.Ar depth
+directories deep.
+.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 ENVIRONMENT VARIABLES
+.Bl -tag -width BLOCKSIZE
+.It Ev BLOCKSIZE
+If the environment variable
+.Ev BLOCKSIZE
+is set, and the
+.Fl k
+option is not specified, 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 v1 .
diff --git a/usr.bin/du/du.c b/usr.bin/du/du.c
new file mode 100644
index 0000000..995860b
--- /dev/null
+++ b/usr.bin/du/du.c
@@ -0,0 +1,247 @@
+/*
+ * 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.5 (Berkeley) 5/4/95";
+#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>
+#include <unistd.h>
+
+int linkchk __P((FTSENT *));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FTS *fts;
+ FTSENT *p;
+ long blocksize;
+ int ftsoptions, listdirs, listfiles, depth;
+ int Hflag, Lflag, Pflag, aflag, ch, notused, rval, sflag, dflag;
+ char **save;
+
+ save = argv;
+ Hflag = Lflag = Pflag = aflag = sflag = dflag = 0;
+ depth = INT_MAX;
+ ftsoptions = FTS_PHYSICAL;
+ while ((ch = getopt(argc, argv, "HLPad:ksx")) != -1)
+ 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 'd':
+ dflag = 1;
+ depth=atoi(optarg);
+ if(errno == ERANGE) {
+ (void)fprintf(stderr, "Invalid argument to option d: %s", optarg);
+ usage();
+ }
+ 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 || dflag)
+ usage();
+ listdirs = listfiles = 1;
+ } else if (sflag) {
+ if (dflag)
+ usage();
+ listdirs = listfiles = 0;
+ } else if (dflag) {
+ listfiles = 0;
+ listdirs = 1;
+ } 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 ((p->fts_level <= depth && 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, "can't allocate memory");
+ files[nfiles].inode = ino;
+ files[nfiles].dev = dev;
+ ++nfiles;
+ return (0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: du [-H | -L | -P] [-a | -s | -d depth] [-k] [-x] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/ee/Artistic b/usr.bin/ee/Artistic
new file mode 100644
index 0000000..fbf7989
--- /dev/null
+++ b/usr.bin/ee/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/Makefile b/usr.bin/ee/Makefile
new file mode 100644
index 0000000..ff64348
--- /dev/null
+++ b/usr.bin/ee/Makefile
@@ -0,0 +1,27 @@
+CFLAGS+= -DCAP -DHAS_NCURSES -DHAS_UNISTD -DHAS_STDARG -DHAS_STDLIB \
+ -DHAS_CTYPE -DHAS_SYS_IOCTL -DHAS_SYS_WAIT -DSLCT_HDR
+
+PROG= ee
+LINKS= ${BINDIR}/ee ${BINDIR}/ree
+MLINKS= ee.1 ree.1
+DPADD= ${LIBNCURSES} ${LIBMYTINFO}
+LDADD= -lncurses -lmytinfo
+
+LANGS= en_US.ISO_8859-1 fr_FR.ISO_8859-1 de_DE.ISO_8859-1
+FILES= ${LANGS:S/$/.ee.cat/}
+CLEANFILES+= ${FILES}
+
+all: ${FILES}
+
+.for lang in ${LANGS}
+${lang}.ee.cat: ${.CURDIR}/nls/${lang}/ee.msg
+ gencat -new ${.TARGET} ${.ALLSRC}
+.endfor
+
+beforeinstall:
+.for lang in ${LANGS}
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \
+ ${lang}.ee.cat ${DESTDIR}${NLSDIR}/${lang}/ee.cat
+.endfor
+
+.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/ee.1 b/usr.bin/ee/ee.1
new file mode 100644
index 0000000..91ca919
--- /dev/null
+++ b/usr.bin/ee/ee.1
@@ -0,0 +1,529 @@
+.\"
+.\"
+.\" To format this reference page, use the command:
+.\"
+.\" nroff -man ee.1
+.\"
+.\" $Header: /home/ncvs/src/usr.bin/ee/ee.1,v 1.3 1996/02/02 00:25:36 mpp 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 beginning 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 operations
+.\"
+.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
+To the left of each menu item is a letter, which if the corresponding
+letter is pressed on the keyboard selects that menu entry.
+.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
+.\"
+.\" save editor configuration
+.\"
+.SS "Save Editor Configuration"
+.PP
+When using this entry from the
+.B settings
+menu, the user may choose to save the current configuration of
+the editor (see \fBInitializing ee from a
+file\fR above) to a file named
+.I .init.ee
+in the current directory or the user's home directory. If a file named
+.I .init.ee
+already exists, it will be renamed
+.IR .init.ee.old .
+.\"
+.\" Caveats
+.\"
+.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, 1995 Hugh Mahon.
+.SH "SEE ALSO"
+.PP
+termcap(5), terminfo(5), environ(7), 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..8f6650cc
--- /dev/null
+++ b/usr.bin/ee/ee.c
@@ -0,0 +1,5128 @@
+/*
+ | 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.6 1996/05/27 20:59:36 joerg Exp $
+ |
+ */
+
+char *ee_copyright_message =
+"Copyright (c) 1986, 1990, 1991, 1992, 1993, 1994, 1995, 1996 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.3 $Revision: 1.1.1.2 $";
+
+#ifdef NCURSE
+#include "new_curse.h"
+#elif HAS_NCURSES
+#include <ncurses.h>
+#else
+#include <curses.h>
+#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 paint_menu P_((struct menu_entries menu_list[], int max_width, int max_height, int list_size, int top_offset, WINDOW *menu_win, int off_start, int vert_size));
+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 dump_ee_conf 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, dump_ee_conf, -1},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+char *mode_strings[10];
+
+#define NUM_MODES_ITEMS 9
+
+struct menu_entries config_dump_menu[] = {
+ {"", NULL, NULL, NULL, NULL, 0},
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, NULL, -1},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+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];
+
+#define MENU_WARN 1
+
+#define max_alpha_char 36
+
+/*
+ | 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;
+char *conf_dump_err_msg;
+char *conf_dump_success_msg;
+char *conf_not_saved_msg;
+char *ree_no_file_msg;
+char *cancel_string;
+char *menu_too_lrg_msg;
+char *more_above_str, *more_below_str;
+
+#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)
+ {
+ if (restrict_mode())
+ {
+ wmove(com_win, 0, 0);
+ werase(com_win);
+ wprintw(com_win, ree_no_file_msg);
+ wrefresh(com_win);
+ edit_abort(0);
+ }
+ 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, (unsigned char)character );
+ return(1);
+ }
+ }
+ else
+ {
+ waddch(window, (unsigned char)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;
+ do
+ {
+ esc_flag = FALSE;
+ in = wgetch(com_win);
+ if (in == -1)
+ exit(0);
+ if (((in == 8) || (in == 127) || (in == KEY_BACKSPACE)) && (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') && (in < 256))
+ {
+ 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'));
+ *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();
+ if (input_file)
+ {
+ 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
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ 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;
+ char ro_flag = FALSE;
+
+ 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);
+ ro_flag = TRUE;
+ }
+ }
+ 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);
+ if (ro_flag)
+ wprintw(com_win, read_only_msg);
+ 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);
+}
+
+static char item_alpha[] = "abcdefghijklmnopqrstuvwxyz0123456789 ";
+
+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 temp;
+ int list_size;
+ int top_offset; /* offset from top where menu items start */
+ int vert_pos; /* vertical position */
+ int vert_size; /* vertical size for menu list item display */
+ int off_start = 1; /* offset from start of menu items to start display */
+
+
+ /*
+ | determine number and width of menu items
+ */
+
+ list_size = 1;
+ while (menu_list[list_size + 1].item_string != NULL)
+ list_size++;
+ max_width = 0;
+ for (counter = 0; counter <= list_size; counter++)
+ {
+ if ((length = strlen(menu_list[counter].item_string)) > max_width)
+ max_width = length;
+ }
+ max_width += 3;
+ max_width = max(max_width, strlen(cancel_string));
+ max_width = max(max_width, max(strlen(more_above_str), strlen(more_below_str)));
+ max_width += 6;
+
+ /*
+ | make sure that window is large enough to handle menu
+ | if not, print error message and return to calling function
+ */
+
+ if (max_width > COLS)
+ {
+ wmove(com_win, 0, 0);
+ werase(com_win);
+ wprintw(com_win, menu_too_lrg_msg);
+ wrefresh(com_win);
+ clear_com_win = TRUE;
+ return(0);
+ }
+
+ top_offset = 0;
+
+ if (list_size > LINES)
+ {
+ max_height = LINES;
+ if (max_height > 11)
+ vert_size = max_height - 8;
+ else
+ vert_size = max_height;
+ }
+ else
+ {
+ vert_size = list_size;
+ max_height = list_size;
+ }
+
+ if (LINES >= (vert_size + 8))
+ {
+ if (menu_list[0].argument != MENU_WARN)
+ max_height = vert_size + 8;
+ else
+ max_height = vert_size + 7;
+ 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);
+
+ paint_menu(menu_list, max_width, max_height, list_size, top_offset, temp_win, off_start, vert_size);
+
+ counter = 1;
+ vert_pos = 0;
+ do
+ {
+ if (off_start > 2)
+ wmove(temp_win, (1 + counter + top_offset - off_start), 3);
+ else
+ wmove(temp_win, (counter + top_offset - off_start), 3);
+
+ wrefresh(temp_win);
+ in = wgetch(temp_win);
+ input = in;
+ if (input == -1)
+ exit(0);
+
+ if (((tolower(input) >= 'a') && (tolower(input) <= 'z')) ||
+ ((input >= '0') && (input <= '9')))
+ {
+ if ((tolower(input) >= 'a') && (tolower(input) <= 'z'))
+ {
+ temp = 1 + tolower(input) - 'a';
+ }
+ else if ((input >= '0') && (input <= '9'))
+ {
+ temp = (2 + 'z' - 'a') + (input - '0');
+ }
+
+ if (temp <= list_size)
+ {
+ input = '\n';
+ counter = temp;
+ }
+ }
+ else
+ {
+ switch (input)
+ {
+ case ' ': /* space */
+ case '\004': /* ^d, down */
+ case KEY_RIGHT:
+ case KEY_DOWN:
+ counter++;
+ if (counter > list_size)
+ counter = 1;
+ break;
+ case '\010': /* ^h, backspace*/
+ case '\025': /* ^u, up */
+ case 127: /* ^?, delete */
+ case KEY_BACKSPACE:
+ case KEY_LEFT:
+ case KEY_UP:
+ counter--;
+ if (counter == 0)
+ counter = list_size;
+ break;
+ case '\033': /* escape key */
+ if (menu_list[0].argument != MENU_WARN)
+ counter = 0;
+ break;
+ case '\014': /* ^l */
+ case '\022': /* ^r, redraw */
+ paint_menu(menu_list, max_width, max_height,
+ list_size, top_offset, temp_win,
+ off_start, vert_size);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (((list_size - off_start) >= (vert_size - 1)) &&
+ (counter > (off_start + vert_size - 3)) &&
+ (off_start > 1))
+ {
+ if (counter == list_size)
+ off_start = (list_size - vert_size) + 2;
+ else
+ off_start++;
+
+ paint_menu(menu_list, max_width, max_height,
+ list_size, top_offset, temp_win, off_start,
+ vert_size);
+ }
+ else if ((list_size != vert_size) &&
+ (counter > (off_start + vert_size - 2)))
+ {
+ if (counter == list_size)
+ off_start = 2 + (list_size - vert_size);
+ else if (off_start == 1)
+ off_start = 3;
+ else
+ off_start++;
+
+ paint_menu(menu_list, max_width, max_height,
+ list_size, top_offset, temp_win, off_start,
+ vert_size);
+ }
+ else if (counter < off_start)
+ {
+ if (counter <= 2)
+ off_start = 1;
+ else
+ off_start = counter;
+
+ paint_menu(menu_list, max_width, max_height,
+ list_size, top_offset, temp_win, off_start,
+ vert_size);
+ }
+ }
+ while ((input != '\r') && (input != '\n') && (counter != 0));
+
+ 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();
+ redraw();
+
+ return(counter);
+}
+
+void
+paint_menu(menu_list, max_width, max_height, list_size, top_offset, menu_win,
+ off_start, vert_size)
+struct menu_entries menu_list[];
+int max_width, max_height, list_size, top_offset;
+WINDOW *menu_win;
+int off_start, vert_size;
+{
+ int counter, temp_int;
+
+ werase(menu_win);
+
+ /*
+ | output top and bottom portions of menu box only if window
+ | large enough
+ */
+
+ if (max_height > vert_size)
+ {
+ wmove(menu_win, 1, 1);
+ if (!nohighlight)
+ wstandout(menu_win);
+ waddch(menu_win, '+');
+ for (counter = 0; counter < (max_width - 4); counter++)
+ waddch(menu_win, '-');
+ waddch(menu_win, '+');
+
+ wmove(menu_win, (max_height - 2), 1);
+ waddch(menu_win, '+');
+ for (counter = 0; counter < (max_width - 4); counter++)
+ waddch(menu_win, '-');
+ waddch(menu_win, '+');
+ wstandend(menu_win);
+ wmove(menu_win, 2, 3);
+ waddstr(menu_win, menu_list[0].item_string);
+ wmove(menu_win, (max_height - 3), 3);
+ if (menu_list[0].argument != MENU_WARN)
+ waddstr(menu_win, cancel_string);
+ }
+ if (!nohighlight)
+ wstandout(menu_win);
+
+ for (counter = 0; counter < (vert_size + top_offset); counter++)
+ {
+ if (top_offset == 4)
+ {
+ temp_int = counter + 2;
+ }
+ else
+ temp_int = counter;
+
+ wmove(menu_win, temp_int, 1);
+ waddch(menu_win, '|');
+ wmove(menu_win, temp_int, (max_width - 2));
+ waddch(menu_win, '|');
+ }
+ wstandend(menu_win);
+
+ if (list_size > vert_size)
+ {
+ if (off_start >= 3)
+ {
+ temp_int = 1;
+ wmove(menu_win, top_offset, 3);
+ waddstr(menu_win, more_above_str);
+ }
+ else
+ temp_int = 0;
+
+ for (counter = off_start;
+ ((temp_int + counter - off_start) < (vert_size - 1));
+ counter++)
+ {
+ wmove(menu_win, (top_offset + temp_int +
+ (counter - off_start)), 3);
+ if (list_size > 1)
+ wprintw(menu_win, "%c) ", item_alpha[min((counter - 1), max_alpha_char)]);
+ waddstr(menu_win, menu_list[counter].item_string);
+ }
+
+ wmove(menu_win, (top_offset + (vert_size - 1)), 3);
+
+ if (counter == list_size)
+ {
+ if (list_size > 1)
+ wprintw(menu_win, "%c) ", item_alpha[min((counter - 1), max_alpha_char)]);
+ wprintw(menu_win, menu_list[counter].item_string);
+ }
+ else
+ wprintw(menu_win, more_below_str);
+ }
+ else
+ {
+ for (counter = 1; counter <= list_size; counter++)
+ {
+ wmove(menu_win, (top_offset + counter - 1), 3);
+ if (list_size > 1)
+ wprintw(menu_win, "%c) ", item_alpha[min((counter - 1), max_alpha_char)]);
+ waddstr(menu_win, menu_list[counter].item_string);
+ }
+ }
+}
+
+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)
+ {
+ str1 = str2 = string;
+ while (*str2 != '\n')
+ str2++;
+ *str2 = (char) NULL;
+
+ if (unique_test(string, init_strings) != 1)
+ continue;
+
+ 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);
+}
+
+/*
+ | Save current configuration to .init.ee file in the current directory.
+ */
+
+void
+dump_ee_conf()
+{
+ FILE *init_file;
+ FILE *old_init_file = NULL;
+ char *file_name = ".init.ee";
+ char *home_dir = "~/.init.ee";
+ char buffer[512];
+ struct stat buf;
+ char *string;
+ int length;
+ int option = 0;
+
+ if (restrict_mode())
+ {
+ return;
+ }
+
+ option = menu_op(config_dump_menu);
+
+ werase(com_win);
+ wmove(com_win, 0, 0);
+
+ if (option == 0)
+ {
+ wprintw(com_win, conf_not_saved_msg);
+ wrefresh(com_win);
+ return;
+ }
+ else if (option == 2)
+ file_name = resolve_name(home_dir);
+
+ /*
+ | If a .init.ee file exists, move it to .init.ee.old.
+ */
+
+ if (stat(file_name, &buf) != -1)
+ {
+ sprintf(buffer, "%s.old", file_name);
+ unlink(buffer);
+ link(file_name, buffer);
+ unlink(file_name);
+ old_init_file = fopen(buffer, "r");
+ }
+
+ init_file = fopen(file_name, "w");
+ if (init_file == NULL)
+ {
+ wprintw(com_win, conf_dump_err_msg);
+ wrefresh(com_win);
+ return;
+ }
+
+ if (old_init_file != NULL)
+ {
+ /*
+ | Copy non-configuration info into new .init.ee file.
+ */
+ while ((string = fgets(buffer, 512, old_init_file)) != NULL)
+ {
+ length = strlen(string);
+ string[length - 1] = (char) NULL;
+
+ if (unique_test(string, init_strings) == 1)
+ {
+ if (compare(string, Echo, FALSE))
+ {
+ fprintf(init_file, "%s\n", string);
+ }
+ }
+ else
+ fprintf(init_file, "%s\n", string);
+ }
+
+ fclose(old_init_file);
+ }
+
+ fprintf(init_file, "%s\n", case_sen ? CASE : NOCASE);
+ fprintf(init_file, "%s\n", expand_tabs ? EXPAND : NOEXPAND);
+ fprintf(init_file, "%s\n", info_window ? INFO : NOINFO );
+ fprintf(init_file, "%s\n", observ_margins ? MARGINS : NOMARGINS );
+ fprintf(init_file, "%s\n", auto_format ? AUTOFORMAT : NOAUTOFORMAT );
+ fprintf(init_file, "%s %s\n", PRINTCOMMAND, print_command);
+ fprintf(init_file, "%s %d\n", RIGHTMARGIN, right_margin);
+ fprintf(init_file, "%s\n", nohighlight ? NOHIGHLIGHT : HIGHLIGHT );
+ fprintf(init_file, "%s\n", eightbit ? EIGHTBIT : NOEIGHTBIT );
+ fprintf(init_file, "%s\n", emacs_keys_mode ? EMACS_string : NOEMACS_string );
+
+ fclose(init_file);
+
+ wprintw(com_win, conf_dump_success_msg, file_name);
+ wrefresh(com_win);
+
+ if ((option == 2) && (file_name != home_dir))
+ {
+ free(file_name);
+ }
+}
+
+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;
+
+ if (test_line == NULL)
+ return(0);
+
+ pnt = test_line->line;
+ if ((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 not_blank;
+ 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.
+ */
+
+ counter = 0;
+
+ while (!leave_loop)
+ {
+ if (position != curr_line->line_length)
+ eol();
+ left(TRUE);
+ if (*point != ' ')
+ {
+ right(TRUE);
+ insert(' ');
+ }
+ else
+ right(TRUE);
+
+ not_blank = FALSE;
+
+ /*
+ | 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();
+
+ /*
+ | We know this line was not blank before, so
+ | make sure that it doesn't have one of the
+ | leading characters that indicate the line
+ | should not be modified.
+ |
+ | We also know that this character should not
+ | be left as the first character of this line.
+ */
+
+ if ((Blank_Line(curr_line)) &&
+ (curr_line->line[0] != '.') &&
+ (curr_line->line[0] != '>'))
+ {
+ del_line();
+ not_blank = FALSE;
+ }
+ else
+ not_blank = TRUE;
+
+ /*
+ | 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();
+ not_blank = TRUE;
+ if (position != 1)
+ bol();
+ left(TRUE);
+ }
+ }
+
+ if ((!Blank_Line(curr_line->next_line)) || (not_blank))
+ {
+ adv_line();
+ counter++;
+ }
+ else
+ leave_loop = TRUE;
+ }
+
+/*
+ | go back to begin of paragraph, put cursor back to original position
+ */
+
+ if (position != 1)
+ bol();
+ while ((counter-- > 0) || (!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 strchr() look-alike for systems without
+ strchr() */
+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 ESC-Enter: exit ee ");
+ 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 ESC-Enter: exit ee ");
+ 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");
+ conf_dump_err_msg = catgetlocal( 162, "unable to open .init.ee for writing, no configuration saved!");
+ conf_dump_success_msg = catgetlocal( 163, "ee configuration saved in file %s");
+ modes_menu[9].item_string = catgetlocal( 164, "save editor configuration");
+ config_dump_menu[0].item_string = catgetlocal( 165, "save ee configuration");
+ config_dump_menu[1].item_string = catgetlocal( 166, "save in current directory");
+ config_dump_menu[2].item_string = catgetlocal( 167, "save in home directory");
+ conf_not_saved_msg = catgetlocal( 168, "ee configuration not saved");
+ ree_no_file_msg = catgetlocal( 169, "must specify a file when invoking ree");
+ cancel_string = catgetlocal( 170, "press Esc to cancel");
+ menu_too_lrg_msg = catgetlocal( 180, "menu too large for window");
+ more_above_str = catgetlocal( 181, "^^more^^");
+ more_below_str = catgetlocal( 182, "VVmoreVV");
+
+ 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.i18n.guide b/usr.bin/ee/ee.i18n.guide
new file mode 100644
index 0000000..0850c2e
--- /dev/null
+++ b/usr.bin/ee/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/ee.msg b/usr.bin/ee/ee.msg
new file mode 100644
index 0000000..6c5ef75
--- /dev/null
+++ b/usr.bin/ee/ee.msg
@@ -0,0 +1,179 @@
+$ This file contains the messages for ee ("easy editor"). See the file
+$ ee.i18n.guide for more information
+$
+$ For ee patchlevel 3
+$
+$ $Header: /home/hugh/sources/old_ae/RCS/ee.msg,v 1.6 1995/10/16 05:20:50 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"
+162 "unable to open .init.ee for writing, no configuration saved!"
+163 "ee configuration saved in file %s"
+164 "save editor configuration"
+165 "save ee configuration"
+166 "save in current directory"
+167 "save in home directory"
+168 "ee configuration not saved"
+169 "must specify a file when invoking ree"
diff --git a/usr.bin/ee/new_curse.c b/usr.bin/ee/new_curse.c
new file mode 100644
index 0000000..91c1078
--- /dev/null
+++ b/usr.bin/ee/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/ncvs/src/usr.bin/ee/doc/new_curse.c,v 1.1.1.1 1995/08/30 07:28:06 jkh 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.1.1.1 $";
+
+#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/new_curse.h b/usr.bin/ee/new_curse.h
new file mode 100644
index 0000000..86a8574
--- /dev/null
+++ b/usr.bin/ee/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/nls/de_DE.ISO8859-1/ee.msg b/usr.bin/ee/nls/de_DE.ISO8859-1/ee.msg
new file mode 100644
index 0000000..aab616d
--- /dev/null
+++ b/usr.bin/ee/nls/de_DE.ISO8859-1/ee.msg
@@ -0,0 +1,182 @@
+$ 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/nls/de_DE.ISO_8859-1/ee.msg,v 1.3 1996/12/29 10:54:30 joerg Exp $
+$
+$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ü ESC-Enter: ee beenden "
+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 ESC-Enter: Ende ee "
+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\n"
+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"
+162 "Kann die Datei .init.ee nicht schreiben, Konfiguration nicht gespeichert!"
+163 "ee-Konfiguration in Datei %s gespeichert"
+164 "speichere Editor-Konfiguration"
+165 "speichere ee-Konfiguration"
+166 "speichern im aktuellen Verzeichnis"
+167 "speichern im Home-Verzeichnis"
+168 "ee-Konfiguration nicht gespeichert"
+169 "beim Aufruf von ree muß ein Dateiname angegeben werden"
+170 "Esc zum Verlassen"
+180 "Menü zu groß für den Bildschirm"
+181 "^^weiter^^"
+182 "VVweiterVV"
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..aab616d
--- /dev/null
+++ b/usr.bin/ee/nls/de_DE.ISO_8859-1/ee.msg
@@ -0,0 +1,182 @@
+$ 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/nls/de_DE.ISO_8859-1/ee.msg,v 1.3 1996/12/29 10:54:30 joerg Exp $
+$
+$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ü ESC-Enter: ee beenden "
+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 ESC-Enter: Ende ee "
+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\n"
+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"
+162 "Kann die Datei .init.ee nicht schreiben, Konfiguration nicht gespeichert!"
+163 "ee-Konfiguration in Datei %s gespeichert"
+164 "speichere Editor-Konfiguration"
+165 "speichere ee-Konfiguration"
+166 "speichern im aktuellen Verzeichnis"
+167 "speichern im Home-Verzeichnis"
+168 "ee-Konfiguration nicht gespeichert"
+169 "beim Aufruf von ree muß ein Dateiname angegeben werden"
+170 "Esc zum Verlassen"
+180 "Menü zu groß für den Bildschirm"
+181 "^^weiter^^"
+182 "VVweiterVV"
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..e1364c0
--- /dev/null
+++ b/usr.bin/ee/nls/en_US.ISO_8859-1/ee.msg
@@ -0,0 +1,182 @@
+$ 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/nls/en_US.ISO_8859-1/ee.msg,v 1.2 1996/05/27 21:00:00 joerg 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 ESC-Enter: exit ee "
+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 ESC-Enter: exit ee "
+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"
+162 "unable to open .init.ee for writing, no configuration saved!"
+163 "ee configuration saved in file %s"
+164 "save editor configuration"
+165 "save ee configuration"
+166 "save in current directory"
+167 "save in home directory"
+168 "ee configuration not saved"
+169 "must specify a file when invoking ree"
+170 "press Esc to cancel"
+180 "menu too large for window"
+181 "^^more^^"
+182 "VVmoreVV"
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..e1364c0
--- /dev/null
+++ b/usr.bin/ee/nls/en_US.US-ASCII/ee.msg
@@ -0,0 +1,182 @@
+$ 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/nls/en_US.ISO_8859-1/ee.msg,v 1.2 1996/05/27 21:00:00 joerg 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 ESC-Enter: exit ee "
+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 ESC-Enter: exit ee "
+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"
+162 "unable to open .init.ee for writing, no configuration saved!"
+163 "ee configuration saved in file %s"
+164 "save editor configuration"
+165 "save ee configuration"
+166 "save in current directory"
+167 "save in home directory"
+168 "ee configuration not saved"
+169 "must specify a file when invoking ree"
+170 "press Esc to cancel"
+180 "menu too large for window"
+181 "^^more^^"
+182 "VVmoreVV"
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..2fa3b27
--- /dev/null
+++ b/usr.bin/ee/nls/fr_FR.ISO8859-1/ee.msg
@@ -0,0 +1,182 @@
+$ 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 ESC-Enter: quitter ee "
+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 "écrire : 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 numé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 "^[ (échap.) 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. ESC-Enter: quitter "
+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 "écrire: 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 " pressez sur échap. (^[) 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] [+numéro_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 "entrez 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 "tapez entrée pour continuer "
+105 "pressez sur échap. pour annuler"
+106 "menu trop grand pour la fenêtre"
+107 "appuyez 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"
+123 "RENUMEROTER"
+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 "^[ (échap.) menu ^y rechercher... ^k efface ligne ^p ligne préc ^g page préc"
+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"
+162 "impossible d'écrire .init.ee, configuration non sauvée !"
+163 "configuration de ee sauvée en %s"
+164 "sauvegarde configuration de l'éditeur"
+165 "sauvegarde configuration de ee"
+166 "sauvegarde dans le répertoire courant"
+167 "sauvegarde dans le répertoire d'acceuil"
+168 "configuration de ee non sauvée"
+169 "nom de fichier manquant pour ree"
+170 "appuyez sur échap. pour annuler"
+180 "menu trop large pour la fenêtre"
+181 "^^encore^^"
+182 "VVencoreVV"
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..2fa3b27
--- /dev/null
+++ b/usr.bin/ee/nls/fr_FR.ISO_8859-1/ee.msg
@@ -0,0 +1,182 @@
+$ 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 ESC-Enter: quitter ee "
+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 "écrire : 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 numé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 "^[ (échap.) 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. ESC-Enter: quitter "
+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 "écrire: 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 " pressez sur échap. (^[) 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] [+numéro_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 "entrez 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 "tapez entrée pour continuer "
+105 "pressez sur échap. pour annuler"
+106 "menu trop grand pour la fenêtre"
+107 "appuyez 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"
+123 "RENUMEROTER"
+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 "^[ (échap.) menu ^y rechercher... ^k efface ligne ^p ligne préc ^g page préc"
+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"
+162 "impossible d'écrire .init.ee, configuration non sauvée !"
+163 "configuration de ee sauvée en %s"
+164 "sauvegarde configuration de l'éditeur"
+165 "sauvegarde configuration de ee"
+166 "sauvegarde dans le répertoire courant"
+167 "sauvegarde dans le répertoire d'acceuil"
+168 "configuration de ee non sauvée"
+169 "nom de fichier manquant pour ree"
+170 "appuyez sur échap. pour annuler"
+180 "menu trop large pour la fenêtre"
+181 "^^encore^^"
+182 "VVencoreVV"
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..8894dd0
--- /dev/null
+++ b/usr.bin/env/env.c
@@ -0,0 +1,90 @@
+/*
+ * 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;
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char **ep, *p;
+ char *cleanenv[1];
+ int ch;
+
+ while ((ch = getopt(argc, argv, "-")) != -1)
+ switch(ch) {
+ case '-':
+ environ = cleanenv;
+ cleanenv[0] = NULL;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ 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);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: env [-] [name=value ...] [command]\n");
+ exit(1);
+}
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..69f182b
--- /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 ``.h''.
+.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..a21c72d
--- /dev/null
+++ b/usr.bin/error/touch.c
@@ -0,0 +1,774 @@
+/*
+ * 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 <sys/param.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[MAXPATHLEN];
+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;
+{
+ int fd;
+
+ 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)strcat(n_name,"error.XXXXXX");
+ fd = mkstemp(n_name);
+ if ( fd < 0 || (n_touchedfile = fdopen(fd, "w")) == NULL) {
+ if (fd >= 0)
+ close(fd);
+ 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..9d92f41
--- /dev/null
+++ b/usr.bin/expand/expand.c
@@ -0,0 +1,184 @@
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)expand.c 8.1 (Berkeley) 6/9/93";
+#else
+static const char rcsid[] =
+ "$Id$";
+#endif
+#endif /* not lint */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * expand - expand tabs to equivalent spaces
+ */
+int nstops;
+int tabstops[100];
+
+static void getstops __P((char *));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c, column;
+ register int n;
+
+ /* handle obsolete syntax */
+ while (argc > 1 && argv[1][0] && isdigit(argv[1][1])) {
+ getstops(&argv[1][1]);
+ argc--; argv++;
+ }
+
+ while ((c = getopt (argc, argv, "t:")) != -1) {
+ switch (c) {
+ case 't':
+ getstops(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ do {
+ if (argc > 0) {
+ if (freopen(argv[0], "r", stdin) == NULL) {
+ perror(argv[0]);
+ exit(1);
+ }
+ argc--, argv++;
+ }
+ column = 0;
+ while ((c = getchar()) != EOF) {
+ 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);
+}
+
+static void
+getstops(cp)
+ register char *cp;
+{
+ register int i;
+
+ nstops = 0;
+ 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 != ',' && *cp != ' ')
+ goto bad;
+ cp++;
+ }
+}
+
+static void
+usage()
+{
+ (void)fprintf (stderr, "usage: expand [-t tablist] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/f2c/Makefile b/usr.bin/f2c/Makefile
new file mode 100644
index 0000000..87a6c34
--- /dev/null
+++ b/usr.bin/f2c/Makefile
@@ -0,0 +1,35 @@
+# 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 conflicts"
+ 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
+
+beforeinstall:
+ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/f2c.h \
+ ${DESTDIR}/usr/include
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/f2c/Notice b/usr.bin/f2c/Notice
new file mode 100644
index 0000000..8db1d7b
--- /dev/null
+++ b/usr.bin/f2c/Notice
@@ -0,0 +1,23 @@
+/****************************************************************
+Copyright 1990 - 1997 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..8267bea
--- /dev/null
+++ b/usr.bin/f2c/README
@@ -0,0 +1,168 @@
+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 any of the source
+files (excluding the makefile), 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@netlib.bell-labs.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.bell-labs.com; for more
+details, ask netlib@netlib.bell-labs.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. In general, if f2c faults
+when you first try to run it, try compiling malloc.c with -DCRAY;
+this is necessary with at least one version of Linux (but not with
+others).
+
+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 .
+
+In general, under Linux it is necessary to compile libI77 with
+-DNON_UNIX_STDIO . Under at least one variant of Linux, you can make
+and install a shared-library version of libf2c by compiling libI77
+with -DNON_UNIX_STDIO, creating libf2c.a as above, and then executing
+
+ mkdir t
+ ln lib?77/*.o t
+ cd t; cc -shared -o ../libf2c.so -Wl,-soname,libf2c.so.1 *.o
+ cd ..
+ rm -r t
+ rm /usr/lib/libf2c*
+ mv libf2c.a libf2c.so /usr/lib
+ cd /usr/lib
+ ln libf2c.so libf2c.so.1
+ ln libf2c.so libf2c.so.1.0.0
+
+On some other systems, /usr/local/lib is the appropriate installation
+directory.
+
+
+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@netlib.bell-labs.com (or use anonymous ftp from
+netlib.bell-labs.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@bell-labs.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@netlib.bell-labs.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..e5bacaa
--- /dev/null
+++ b/usr.bin/f2c/cds.c
@@ -0,0 +1,195 @@
+/****************************************************************
+Copyright 1990, 1993, 1994 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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..7454039
--- /dev/null
+++ b/usr.bin/f2c/data.c
@@ -0,0 +1,493 @@
+/****************************************************************
+Copyright 1990, 1993 - 1996 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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)
+ {
+ if (lineno != err_lineno)
+ 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;
+ Constp 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 = mkconst(TYCHAR);
+ qc->Const = q->addrblock.user.Const;
+ qc->vleng = q->addrblock.vleng;
+ q->addrblock.vleng = 0;
+ frexpr(q);
+ p->paramval = 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..1ed4537
--- /dev/null
+++ b/usr.bin/f2c/defines.h
@@ -0,0 +1,300 @@
+#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 */
+#define OPBITTEST 62 /* btest */
+#define OPBITCLR 63 /* ibclr */
+#define OPBITSET 64 /* ibset */
+#define OPQBITCLR 65 /* ibclr, integer*8 */
+#define OPQBITSET 66 /* ibset, integer*8 */
+#define OPBITBITS 67 /* ibits */
+#define OPBITSH 68 /* ishft */
+#define OPBITSHC 69 /* ishftc */
+
+/* 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) */
+#define INTRBGEN 8 /* bit manipulation */
+
+
+/* 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 */
+#define MAXNAMELEN 50 /* Maximum Fortran name length */
+
+/* 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..2d80862
--- /dev/null
+++ b/usr.bin/f2c/defs.h
@@ -0,0 +1,1055 @@
+/****************************************************************
+Copyright 1990 - 1996 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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 err_lineno, 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 long 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 doinit; /* for use with -onetrip */
+ 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:4;
+ 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 lituse; /* usage count */
+ long litnum; /* numeric part of the assembler
+ label for this constant value */
+ 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));
+long 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 sserr Argdcl((Namep));
+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/equiv.c b/usr.bin/f2c/equiv.c
new file mode 100644
index 0000000..0b7c94c
--- /dev/null
+++ b/usr.bin/f2c/equiv.c
@@ -0,0 +1,413 @@
+/****************************************************************
+Copyright 1990, 1993-6 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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;
+ if (!(primp = q->eqvitem.eqvlhs))
+ continue;
+ 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);
+}
+
+ struct Primblock *
+#ifdef KR_headers
+primchk(e) expptr e;
+#else
+primchk(expptr e)
+#endif
+{
+ if (e->headblock.tag != TPRIM) {
+ err("Invalid name in EQUIVALENCE.");
+ return 0;
+ }
+ return &e->primblock;
+ }
diff --git a/usr.bin/f2c/error.c b/usr.bin/f2c/error.c
new file mode 100644
index 0000000..0899d82
--- /dev/null
+++ b/usr.bin/f2c/error.c
@@ -0,0 +1,347 @@
+/****************************************************************
+Copyright 1990, 1993, 1994 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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..5e3d7b2
--- /dev/null
+++ b/usr.bin/f2c/exec.c
@@ -0,0 +1,934 @@
+/****************************************************************
+Copyright 1990, 1993 - 1996 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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 */
+
+ ctlstack->doinit = 0;
+ 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 {
+ if (onetripflag)
+ ctlstack->doinit = doinit = (expptr) mktmp0(dotype, ENULL);
+ 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),
+ ctlstack->doinit ? cpexpr(doinit) : 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);
+ if (e = ctlstack->doinit)
+ frtemp((Addrp)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..59ea9b6
--- /dev/null
+++ b/usr.bin/f2c/expr.c
@@ -0,0 +1,3436 @@
+/****************************************************************
+Copyright 1990 - 1996 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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
+ || p->tag == TADDR && p->addrblock.uname_tag == UNAM_CONST)
+ {
+ 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 );
+ if (p->tag == TADDR)
+ q->constblock.vstg = p->addrblock.user.kludge.vstg1;
+ 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;
+ continue;
+ }
+
+/* Take a function name and turn it into an Addr. This only happens when
+ nothing else has figured out the function beforehand */
+
+ if (qtag == TPRIM && q->primblock.argsp == 0) {
+ if (q->primblock.namep->vclass==CLPROC
+ && q->primblock.namep->vprocclass != PTHISPROC) {
+ p->datap = (char *)mkaddr(q->primblock.namep);
+ continue;
+ }
+
+ if (q->primblock.namep->vdim != NULL) {
+ p->datap = (char *)mkscalar(q->primblock.namep);
+ if ((q->primblock.fcharp||q->primblock.lcharp)
+ && (q->primblock.namep->vtype != TYCHAR
+ || q->primblock.namep->vdim))
+ sserr(q->primblock.namep);
+ continue;
+ }
+
+ if (q->primblock.namep->vdovar
+ && (t = (tagptr) memversion(q->primblock.namep))) {
+ p->datap = (char *)fixtype(t);
+ continue;
+ }
+ }
+ 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)
+ sserr(np);
+ else {
+ if(p->lcharp == NULL)
+ p->lcharp = (expptr)(
+ /* s->vleng == 0 only with errors */
+ s->vleng ? cpexpr(s->vleng) : ICON(1));
+ 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 OPBITTEST:
+ case OPBITCLR:
+ case OPBITSET:
+#ifdef TYQUAD
+ case OPQBITCLR:
+ case OPQBITSET:
+#endif
+
+ 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")
+ case OPBITTEST:
+ 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 OPBITCLR:
+ case OPBITSET:
+ if (lt < TYLONG)
+ lt = TYLONG;
+ return(lt);
+#ifdef TYQUAD
+ case OPQBITCLR:
+ case OPQBITSET:
+ return TYQUAD;
+#endif
+
+ 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 = (unsigned long)lp->constblock.Const.ci >>
+ rp->constblock.Const.ci;
+ break;
+
+ case OPBITTEST:
+ p->Const.ci = (lp->constblock.Const.ci &
+ 1L << rp->constblock.Const.ci) != 0;
+ break;
+
+ case OPBITCLR:
+ p->Const.ci = lp->constblock.Const.ci &
+ ~(1L << rp->constblock.Const.ci);
+ break;
+
+ case OPBITSET:
+ p->Const.ci = lp->constblock.Const.ci |
+ 1L << 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)
+ {
+ n = -n;
+ if( ISINT(type) )
+ {
+ switch(ap->Const.ci) {
+ case 0:
+ err("0 ** negative number");
+ return;
+ case 1:
+ case -1:
+ goto mult;
+ }
+ err("integer ** negative number");
+ return;
+ }
+ else if (!x0.Const.cd[0]
+ && (!ISCOMPLEX(type) || !x0.Const.cd[1])) {
+ err("0.0 ** negative number");
+ return;
+ }
+ consbinop(OPSLASH, type, &x, p, &x0);
+ }
+ else
+ mult: 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;
+ case TYLOGICAL:
+ k = ap->ci - bp->ci;
+ }
+
+ 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;
+ }
+}
+
+
+ void
+#ifdef KR_headers
+sserr(np) Namep np;
+#else
+sserr(Namep np)
+#endif
+{
+ errstr(np->vtype == TYCHAR
+ ? "substring of character array %.70s"
+ : "substring of noncharacter %.73s", np->fvarname);
+ }
diff --git a/usr.bin/f2c/f2c.1 b/usr.bin/f2c/f2c.1
new file mode 100644
index 0000000..4011fcb
--- /dev/null
+++ b/usr.bin/f2c/f2c.1
@@ -0,0 +1,301 @@
+.\" mdoc translation of the f2c.1 manpage (deprecated -man format) supplied
+.\" with f2c. The original manpage did not have a copyright statement, but
+.\" the file /usr/src/bin/f2c/Notice states:
+.\"
+.\"/****************************************************************
+.\"Copyright 1990 - 1997 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.
+.\"****************************************************************/
+.\"
+.Dd April 19, 1996
+.Os "AT&T Bell Lab and Bellcore"
+.Dt F2C 1
+.Sh NAME
+.Nm f2c
+.Nd Convert Fortran 77 to C or C++
+.Sh SYNOPSIS
+.Nm f2c
+.Op Fl AaCcEfgpRrsUuw
+.Op Fl C++
+.Op Fl cd
+.Op Fl d Ar dir
+.Op Fl ec
+.Op Fl e1c
+.Op Fl ext
+.Op Fl h Ns Op Cm d
+.Op Fl \&I2
+.Op Fl \&i2
+.Op Fl i90
+.Op Fl kr Ns Op Cm d
+.Op Fl o Ar name
+.Op Fl onetrip
+.Op Fl P Ns Op Cm s
+.Op Fl r8
+.Op Fl 72
+.Op Fl T Ar dir
+.Op Fl w8
+.Op Fl W Ns Ar n
+.Op Fl z
+.Op Fl !bs
+.Op Fl !c
+.Op Fl !I
+.Op Fl !i8
+.Op Fl !it
+.Op Fl !P
+.Ar file ...
+.Sh DESCRIPTION
+.Nm F2c
+converts Fortran 77 source code in
+.Ar files
+with names ending in
+.So \&.f Sc
+or
+.So \&.F Sc
+to C (or C++) source files in the current directory, with
+.So \&.c Sc
+substituted for the final
+.So \&.f Sc
+or
+.So \&.F Sc .
+If no Fortran files are named,
+.Nm f2c
+reads Fortran from standard input and writes C on standard output.
+.Ar File
+names that end with
+.So \&.p Sc
+or
+.So \&.P Sc
+are taken to be prototype files, as produced by option
+.Fl P ,
+and are read first.
+.Sh OPTIONS
+.Bl -tag -width flag
+.It Fl A
+Produce ANSI C. Default is old-style C.
+.It Fl a
+Make local variables automatic rather than static unless they appear in a
+DATA , EQUIVALENCE , NAMELIST , or SAVE statement.
+.It Fl C
+Compile code to check that subscripts are within declared array bounds.
+.It Fl C++
+Output C++ code.
+.It Fl c
+Include original Fortran source as comments.
+.It Fl cd
+Do not recognize cdabs, cdcos, cdexp, cdlog, cdsin, and
+cdsqrt as synonyms for the double complex intrinsics
+zabs, zcos, zexp, zlog, zsin, and zsqrt, respectively.
+.It Fl d Ar dir
+Write `.c' files in directory
+.Ar dir
+instead of the current directory
+.It Fl E
+Declare uninitialized COMMON to be Extern (overridably defined in
+.Pa f2c.h
+as
+.Em extern
+).
+.It Fl ec
+Place uninitialized COMMON blocks in separate files:
+COMMON ABC appears in file abc_com.c .
+Option
+.Fl e1c
+bundles the separate files
+into the output file, with comments that give an unbundling
+.Xr sed 1
+script.
+.It Fl e1c
+See
+.Fl ec .
+.It Fl ext
+Complain about Fortran 77 extensions.
+.It Fl f
+Assume free-format input: accept text after column 72 and do not
+pad fixed-format lines shorter than 72 characters with blanks.
+.It Fl 72
+Treat text appearing after column 72 as an error.
+.It Fl g
+Include original Fortran line numbers in
+.Sy #line
+lines.
+.It Fl h Ns Op Cm d
+Emulate Fortran 66's treatment of Hollerith: try to align character strings on
+word (or, if the option is
+.Fl hd ,
+on double-word) boundaries.
+.It Fl \&I2
+Render INTEGER and LOGICAL as short, INTEGER*4 as long int. Assume the
+default
+.Em libF77
+and
+.Em libI77
+allow only INTEGER*4 (and no LOGICAL) variables in INQUIREs. Option
+.Fl \&I4
+confirms the default rendering of INTEGER as long int.
+.It Fl \&i2
+Similar to
+.Fl \&I2 ,
+but assume a modified
+.Em libF77
+and
+.Em libI77
+(compiled with
+.Fl Df2c_i2 ),
+so INTEGER and LOGICAL variables may be assigned by INQUIRE and array lengths
+are stored in short ints
+.It Fl i90
+Do not recognize the Fortran 90 bit-manipulation intrinsics btest,
+iand, ibclr, ibits, ibset, ieor, ior, ishft, and ishftc.
+.It Fl kr Ns Op Cm d
+Use temporary values to enforce Fortran expression evaluation
+where K&R (first edition) parenthesization rules allow rearrangement.
+If the option is
+.Fl krd ,
+use double precision temporaries even for single-precision operands.
+.It Fl o Ar name
+The C source code is written into file
+.Ar name .
+.It Fl 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.)
+.It Fl P Ns Op Cm s
+Write a
+.Ar file Ns \&.P
+of ANSI (or C++) prototypes for definitions in each input
+.Ar file Ns \&.f
+or
+.Ar file Ns \&.F .
+When reading Fortran from standard input, write prototypes at the beginning of
+standard output. Option
+.Fl Ps
+implies
+.Fl P
+and gives exit status 4 if rerunning
+.Nm f2c
+may change prototypes or declarations.
+.It Fl p
+Supply preprocessor definitions to make common-block members look like local
+variables.
+.It Fl R
+Do not promote REAL functions and operations to DOUBLE PRECISION. Option
+.Fl !R
+confirms the default, which imitates Fortran 77.
+.It Fl r
+Cast values of REAL functions (including intrinsics) to REAL.
+.It Fl r8
+Promote REAL to DOUBLE PRECISION, COMPLEX to DOUBLE COMPLEX.
+.It Fl s
+Preserve multidimensional subscripts.
+.It Fl T Ar dir
+Put temporary files in directory
+.Ar dir .
+.It Fl U
+Honor the case of variable and external names. Fortran keywords must be in
+.Em lower
+case.
+.It Fl u
+Make the default type of a variable
+.So undefined Sc
+rather than using the default Fortran rules.
+.It Fl w
+Suppress all warning messages. If the option is
+.Fl w66 ,
+only Fortran 66 compatibility warnings are suppressed.
+.It Fl w8
+Suppress warnings when COMMON or EQUIVALENCE forces odd-word alignment of
+doubles.
+.It Fl W Ns Ar n
+Assume
+.Ar n
+characters/word (default 4) when initializing numeric variables with character
+data.
+.It Fl z
+Do not implicitly recognize DOUBLE COMPLEX.
+.It Fl !bs
+Do not recognize
+.Em backslash
+escapes
+(\e", \e', \e0, \e\e, \eb, \ef, \en, \er, \et, \ev) in character strings.
+.It Fl !c
+Inhibit C output, but produce
+.Fl P
+output.
+.It Fl !I
+Reject
+.Sy include
+statements.
+.It Fl !i8
+Disallow INTEGER*8.
+.It Fl !it
+Don't infer types of untyped EXTERNAL procedures from use as parameters to
+previously defined or prototyped procedures.
+.It Fl !P
+Do not attempt to infer ANSI or C++ prototypes from usage.
+.El
+.Pp
+Object code should be loaded by with
+.Xr ld 1
+or
+.Xr cc 1
+and the following libraries need to specified:
+.Fl lf2c lm .
+.Sh FILES
+.Ar file Ns \&.[fF]
+input file
+
+.Ar file Ns \&.c
+output file
+
+.Pa /usr/include/f2c.h
+header file
+
+.Pa /usr/lib/libf2c.a
+intrinsic function library and Fortran 77 I/O library
+
+.Sh "SEE ALSO"
+.Rs
+.%A S. I. Feldman
+.%A P. J. Weinberger
+.%T A Portable Fortran 77 Compiler
+.%B UNIX Time Sharing System Programmer's Manual
+.%V Volume 2
+.%D 1990
+.%O AT&T Bell Laboratories
+.%N Tenth Edition
+.Re
+.Sh DIAGNOSTICS
+The diagnostics produced by
+.Nm f2c
+are intended to be
+self-explanatory.
+.Sh BUGS
+Floating-point constant expressions are simplified in
+the floating-point arithmetic of the machine running
+.Nm f2c
+so they are typically accurate to at most 16 or 17 decimal places.
+.Pp
+Untypable EXTERNAL functions are declared int.
+.Pp
+There are several undocumented valid options for
+.Nm f2c .
+These options are discussed at the top of
+.Pa /usr/src/usr.bin/f2c/main.c .
diff --git a/usr.bin/f2c/f2c.h b/usr.bin/f2c/f2c.h
new file mode 100644
index 0000000..6514cd9
--- /dev/null
+++ b/usr.bin/f2c/f2c.h
@@ -0,0 +1,223 @@
+/* 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 unsigned long uinteger;
+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;
+#if 0 /* Adjust for integer*8. */
+typedef long long longint; /* system-dependent */
+typedef unsigned long long ulongint; /* system-dependent */
+#define qbit_clear(a,b) ((a) & ~((ulongint)1 << (b)))
+#define qbit_set(a,b) ((a) | ((ulongint)1 << (b)))
+#endif
+
+#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)
+#define bit_test(a,b) ((a) >> (b) & 1)
+#define bit_clear(a,b) ((a) & ~((uinteger)1 << (b)))
+#define bit_set(a,b) ((a) | ((uinteger)1 << (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/format.c b/usr.bin/f2c/format.c
new file mode 100644
index 0000000..bcd9b99
--- /dev/null
+++ b/usr.bin/f2c/format.c
@@ -0,0 +1,2524 @@
+/****************************************************************
+Copyright 1990 - 1996 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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
+{
+ long 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;
+ char *comma, *type;
+ int did_one = 0;
+
+ cp = assigned_fmts = revchain(assigned_fmts);
+ nice_printf(outfile, "/* Assigned format variables */\n");
+ do {
+ np = (Namep)cp->datap;
+ if (did_one == np->vstg) {
+ comma = ", ";
+ type = "";
+ }
+ else {
+ comma = did_one ? ";\n" : "";
+ type = np->vstg == STGAUTO ? "char " : "static char ";
+ did_one = np->vstg;
+ }
+ nice_printf(outfile, "%s%s*%s_fmt", comma, type, 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
+{ /* with sufficiently illegal input, ei_next == ei_last == 0 is possible */
+ if (ei_next < ei_last && *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..501463a
--- /dev/null
+++ b/usr.bin/f2c/formatdata.c
@@ -0,0 +1,1166 @@
+/****************************************************************
+Copyright 1990, 1991, 1993-6 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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, 0 /* for TYBLANK */ };
+ 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.dcl b/usr.bin/f2c/gram.dcl
new file mode 100644
index 0000000..b30a45c
--- /dev/null
+++ b/usr.bin/f2c/gram.dcl
@@ -0,0 +1,416 @@
+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 = primchk($1);
+ }
+ | equivlist SCOMMA lhs
+ { $$=ALLOC(Eqvchain);
+ $$->eqvitem.eqvlhs = primchk($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;
+ struct Primblock *pp = (struct Primblock *)$1;
+ int tt = $1->tag;
+ if (tt != TPRIM) {
+ if (tt == TCONST)
+ err("parameter in data statement");
+ else
+ erri("tag %d in data statement",tt);
+ $$ = 0;
+ err_lineno = lineno;
+ break;
+ }
+ np = pp -> namep;
+ vardcl(np);
+ if ((pp->fcharp || pp->lcharp)
+ && (np->vtype != TYCHAR || np->vdim))
+ sserr(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) {
+ errstr(np->vstg == STGARG
+ ? "Dummy argument \"%.60s\" in data statement."
+ : "Cannot give data to \"%.75s\"",
+ np->fvarname);
+ $$ = 0;
+ err_lineno = lineno;
+ break;
+ }
+ $$ = 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..39d7e42
--- /dev/null
+++ b/usr.bin/f2c/gram.exec
@@ -0,0 +1,143 @@
+exec: iffable
+ | SDO end_spec label opt_comma dospecw
+ {
+ if($3->labdefined)
+ execerr("no backward DO loops", CNULL);
+ $3->blklevel = blklevel+1;
+ exdo($3->labelno, NPNULL, $5);
+ }
+ | 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..183dfeb
--- /dev/null
+++ b/usr.bin/f2c/gram.head
@@ -0,0 +1,291 @@
+/****************************************************************
+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 */
+extern struct Primblock *primchk Argdcl((expptr));
+
+#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/init.c b/usr.bin/f2c/init.c
new file mode 100644
index 0000000..bc0dff4
--- /dev/null
+++ b/usr.bin/f2c/init.c
@@ -0,0 +1,517 @@
+/****************************************************************
+Copyright 1990, 1992 - 1996 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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 err_lineno;
+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;
+long 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..c83325f
--- /dev/null
+++ b/usr.bin/f2c/intr.c
@@ -0,0 +1,977 @@
+/****************************************************************
+Copyright 1990, 1992, 1994-6 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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 */
+ char extflag; /* for -cd, -i90 */
+ };
+
+/* 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 },
+
+"cdabs", { INTRSPEC, TYDREAL, 13, 1, 0, 1 },
+"cdsqrt", { INTRSPEC, TYDCOMPLEX, 38, 1, 0, 1 },
+"cdexp", { INTRSPEC, TYDCOMPLEX, 42, 1, 0, 1 },
+"cdlog", { INTRSPEC, TYDCOMPLEX, 46, 1, 0, 1 },
+"cdsin", { INTRSPEC, TYDCOMPLEX, 52, 1, 0, 1 },
+"cdcos", { INTRSPEC, TYDCOMPLEX, 56, 1, 0, 1 },
+
+"iand", { INTRBOOL, TYUNKNOWN, OPBITAND, 0, 0, 2 },
+"ior", { INTRBOOL, TYUNKNOWN, OPBITOR, 0, 0, 2 },
+"ieor", { INTRBOOL, TYUNKNOWN, OPBITXOR, 0, 0, 2 },
+
+"btest", { INTRBGEN, TYLOGICAL, OPBITTEST,0, 0, 2 },
+"ibclr", { INTRBGEN, TYUNKNOWN, OPBITCLR, 0, 0, 2 },
+"ibset", { INTRBGEN, TYUNKNOWN, OPBITSET, 0, 0, 2 },
+"ibits", { INTRBGEN, TYUNKNOWN, OPBITBITS,0, 0, 2 },
+"ishft", { INTRBGEN, TYUNKNOWN, OPBITSH, 0, 0, 2 },
+"ishftc", { INTRBGEN, TYUNKNOWN, OPBITSHC, 0, 0, 2 },
+
+"" };
+
+
+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;
+ char *s;
+ static char bit_bits[] = "?bit_bits",
+ bit_shift[] = "?bit_shift",
+ bit_cshift[] = "?bit_cshift";
+ static char *bitop[3] = { bit_bits, bit_shift, bit_cshift };
+ static int t_pref[2] = { 'l', 'q' };
+
+ 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 INTRBGEN:
+ op = f3field;
+ if( ! ONEOF(mtype, MSKINT) )
+ goto badtype;
+ if (op < OPBITBITS) {
+ if(nargs != 2)
+ goto badnargs;
+ if (op != OPBITTEST) {
+#ifdef TYQUAD
+ if (mtype == TYQUAD)
+ op += 2;
+#endif
+ goto intrbool2;
+ }
+ q = mkexpr(op, (expptr)argsp->listp->datap,
+ (expptr)argsp->listp->nextp->datap);
+ q->exprblock.vtype = TYLOGICAL;
+ goto intrbool2a;
+ }
+ if (nargs != 2 && (nargs != 3 || op == OPBITSH))
+ goto badnargs;
+ cp = argsp->listp;
+ ep = (expptr)cp->datap;
+ if (ep->headblock.vtype < TYLONG)
+ cp->datap = (char *)mkconv(TYLONG, ep);
+ while(cp->nextp) {
+ cp = cp->nextp;
+ ep = (expptr)cp->datap;
+ if (ep->headblock.vtype != TYLONG)
+ cp->datap = (char *)mkconv(TYLONG, ep);
+ }
+ if (op == OPBITSH) {
+ ep = (expptr)argsp->listp->nextp->datap;
+ if (ISCONST(ep)) {
+ if ((i = ep->constblock.Const.ci) < 0) {
+ q = (expptr)argsp->listp->datap;
+ if (ISCONST(q)) {
+ ep->constblock.Const.ci = -i;
+ op = OPRSHIFT;
+ goto intrbool2;
+ }
+ }
+ else {
+ op = OPLSHIFT;
+ goto intrbool2;
+ }
+ }
+ }
+ else if (nargs == 2) {
+ if (op == OPBITBITS)
+ goto badnargs;
+ cp->nextp = mkchain((char*)ICON(-1), 0);
+ }
+ ep = (expptr)argsp->listp->datap;
+ i = ep->headblock.vtype;
+ s = bitop[op - OPBITBITS];
+ *s = t_pref[i - TYLONG];
+ ap = builtin(i, s, 1);
+ return fixexpr((Exprp)
+ mkexpr(OPCCALL, (expptr)ap, (expptr)argsp) );
+
+ 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;
+ intrbool2:
+ q = mkexpr(op, (expptr)argsp->listp->datap,
+ (expptr)argsp->listp->nextp->datap);
+ }
+ intrbool2a:
+ 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;
+ int i;
+ extern int intr_omit;
+
+ for(p = intrtab; p->intrval.intrgroup!=INTREND ; ++p)
+ {
+ if( !strcmp(s, p->intrfname) )
+ {
+ if (i = p->intrval.extflag) {
+ if (i & intr_omit)
+ return 0;
+ if (noextflag)
+ errext(s);
+ }
+ 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:
+ case INTRBGEN:
+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..12ecedd
--- /dev/null
+++ b/usr.bin/f2c/io.c
@@ -0,0 +1,1508 @@
+/****************************************************************
+Copyright 1990, 1991, 1993, 1994, 1996 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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 byterev, 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 = (int)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 = (int)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"
+ : !byterev ? "do_uio"
+ : ONEOF(type, M(TYCHAR)|M(TYINT1)|M(TYLOGICAL1))
+ ? "do_ucio" : "do_unio";
+ 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..6e779e1
--- /dev/null
+++ b/usr.bin/f2c/lex.c
@@ -0,0 +1,1707 @@
+/****************************************************************
+Copyright 1990, 1992 - 1997 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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;
+
+ err_lineno = -1;
+ if(inclp)
+ {
+ inclp->incllno = thislin;
+ inclp->inclcode = code;
+ inclp->inclstno = nxtstno;
+ if(nextcd && (j = endcd - nextcd) > 0)
+ inclp->incllinp = copyn(inclp->incllen = j, 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;
+ err_lineno = -1;
+ 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;
+ }
+ if (tokno == 2 && stkey == SDO) {
+ intonly = 1;
+ retval = gettok();
+ intonly = 0;
+ }
+ else
+ 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 & (Table_size-1)])
+ {
+ 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 > sbuf && j1[-1] == MYQUOTE)
+ --j1;
+ 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 string must be preceded by a punctuation mark.
+ '*' is possible only as repetition factor in a data statement
+ not, in particular, in character*2h .
+ To avoid some confusion with missing commas in FORMAT statements,
+ treat a preceding string as a punctuation mark.
+ */
+
+ if( !(*j0=='*'&&sbuf[0]=='d') && *j0!='/'
+ && *j0!='(' && *j0!=',' && *j0!='=' && *j0!='.'
+ && *j0 != MYQUOTE)
+ 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;
+ static char Exp_mi[] = "X**-Y treated as X**(-Y)",
+ Exp_pl[] = "X**+Y treated as X**(+Y)";
+
+ 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 '/':
+ switch(*nextch) {
+ case '/':
+ nextch++;
+ val = SCONCAT;
+ break;
+ case '=':
+ goto sne;
+ default:
+ if (new_dcl && parlev == 0)
+ val = SSLASHD;
+ }
+ return val;
+ case '*':
+ if (*nextch == '*') {
+ nextch++;
+ if (noextflag
+ && nextch <= lastch)
+ switch(*nextch) {
+ case '-':
+ errext(Exp_mi);
+ break;
+ case '+':
+ errext(Exp_pl);
+ }
+ return SPOWER;
+ }
+ break;
+ case '<':
+ switch(*nextch) {
+ case '=':
+ nextch++;
+ val = SLE;
+ break;
+ case '>':
+ sne:
+ 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 > MAXNAMELEN)
+ {
+ char buff[MAXNAMELEN+50];
+ sprintf(buff, toklen >= MAXNAMELEN+10
+ ? "name %.*s... too long, truncated to %.*s"
+ : "name %s too long, truncated to %.*s",
+ MAXNAMELEN+6, token, MAXNAMELEN, token);
+ err(buff);
+ toklen = MAXNAMELEN;
+ token[MAXNAMELEN] = '\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..4183855
--- /dev/null
+++ b/usr.bin/f2c/main.c
@@ -0,0 +1,708 @@
+/****************************************************************
+Copyright 1990 - 1996 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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;
+
+int badargs;
+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;
+flag byterev = NO;
+int intr_omit;
+static int no_cd, no_i90;
+#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 dneg = NO; /* f77 treatment of unary minus */
+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),
+ f2c_entry ("cd", P_NO_ARGS, P_INT, &no_cd, 1),
+ f2c_entry ("i90", P_NO_ARGS, P_INT, &no_i90, 2),
+#ifdef TYQUAD
+ f2c_entry ("!i8", P_NO_ARGS, P_INT, &use_tyquad, NO),
+#endif
+
+ /* options omitted from man pages */
+
+ /* -b ==> for unformatted I/O, call do_unio (for noncharacter */
+ /* data of length > 1 byte) and do_ucio (for the rest) rather */
+ /* than do_uio. This permits modifying libI77 to byte-reverse */
+ /* numeric data. */
+
+ f2c_entry ("b", P_NO_ARGS, P_INT, &byterev, YES),
+
+ /* -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),
+
+ /* -dneg ==> under (default) -!R, imitate f77's bizarre */
+ /* treatment of unary minus of REAL expressions by */
+ /* promoting them to DOUBLE PRECISION . */
+
+ f2c_entry ("dneg", P_NO_ARGS, P_INT, &dneg, 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";
+ }
+ else
+ dneg = 0;
+
+ 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 (badargs)
+ return 1;
+ intr_omit = no_cd | no_i90;
+ 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) {
+ if (debugflag != 1) {
+ if (!o_coutput)
+ coutput = c_name(file_name,'c');
+ else
+ coutput = o_coutput;
+ if (Castargs1 >= 2)
+ proto_fname = c_name(file_name,'P');
+ }
+ cdfilename = coutput;
+ 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();
+ nerr = 1;
+ }
+ 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/malloc.c b/usr.bin/f2c/malloc.c
new file mode 100644
index 0000000..3f5cb2a
--- /dev/null
+++ b/usr.bin/f2c/malloc.c
@@ -0,0 +1,165 @@
+/****************************************************************
+Copyright 1990, 1994 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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;
+ 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..4e3d777
--- /dev/null
+++ b/usr.bin/f2c/mem.c
@@ -0,0 +1,268 @@
+/****************************************************************
+Copyright 1990, 1991, 1994 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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..4d6ab47
--- /dev/null
+++ b/usr.bin/f2c/memset.c
@@ -0,0 +1,66 @@
+/****************************************************************
+Copyright 1990 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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..f5cca53
--- /dev/null
+++ b/usr.bin/f2c/misc.c
@@ -0,0 +1,1329 @@
+/****************************************************************
+Copyright 1990, 1992 - 1995 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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 = (int)newlabel();
+ lp->blklevel = 0;
+ lp->labused = NO;
+ lp->fmtlabused = NO;
+ lp->labdefined = NO;
+ lp->labinacc = NO;
+ lp->labtype = LABUNKNOWN;
+ lp->fmtstring = 0;
+ return(lp);
+}
+
+ long
+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..b0e1058
--- /dev/null
+++ b/usr.bin/f2c/names.c
@@ -0,0 +1,835 @@
+/****************************************************************
+Copyright 1990, 1992 - 1996 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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];
+ char *fmt = "%s_len", *s = arg->fvarname;
+ switch(*s) {
+ case 'r':
+ if (!strcmp(s+1, "et_val"))
+ goto adjust_fmt;
+ break;
+ case 'h':
+ case 'i':
+ if (!s[1]) {
+ adjust_fmt:
+ fmt = "%s_length"; /* avoid conflict with libF77 */
+ }
+ }
+ sprintf (buf, fmt, s);
+ 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%ld", 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%ld", 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%ld", 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%ld", 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", "uinteger",
+ "ulongint", "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..0d5f5cc
--- /dev/null
+++ b/usr.bin/f2c/niceprintf.c
@@ -0,0 +1,441 @@
+/****************************************************************
+Copyright 1990, 1991, 1993, 1994 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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/output.c b/usr.bin/f2c/output.c
new file mode 100644
index 0000000..03d0ed0
--- /dev/null
+++ b/usr.bin/f2c/output.c
@@ -0,0 +1,1709 @@
+/****************************************************************
+Copyright 1990 - 1996 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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)" },
+ /* OPBITTEST 62 */ { BINARY_OP, 0, "bit_test(%l,%r)" },
+ /* OPBITCLR 63 */ { BINARY_OP, 0, "bit_clear(%l,%r)" },
+ /* OPBITSET 64 */ { BINARY_OP, 0, "bit_set(%l,%r)" },
+#ifdef TYQUAD
+ /* OPQBITCLR 65 */ { BINARY_OP, 0, "qbit_clear(%l,%r)" },
+ /* OPQBITSET 66 */ { BINARY_OP, 0, "qbit_set(%l,%r)" },
+#endif
+
+/* 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)
+
+extern int dneg;
+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, long, 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, 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;
+ long memno;
+ Constp cp;
+#else
+output_literal(FILE *fp, long 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 && dneg) {
+ 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 conversions, ichar and character*1 */
+ register expptr lp;
+ register union Expression *Offset;
+ register char *cp;
+ int lt;
+ char buf[8], *s;
+ unsigned int k;
+ Namep np;
+ Addrp ap;
+
+ 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) {
+ s = c_type_decl(e->vtype, 0);
+ if (ISCOMPLEX(lt)) {
+ tryagain:
+ np = (Namep)e->leftp;
+ switch(np->tag) {
+ case TNAME:
+ nice_printf(fp, "(%s) %s%sr", s,
+ np->cvarname,
+ np->vstg == STGARG ? "->" : ".");
+ return 1;
+ case TADDR:
+ ap = (Addrp)np;
+ switch(ap->uname_tag) {
+ case UNAM_IDENT:
+ nice_printf(fp, "(%s) %s.r", s,
+ ap->user.ident);
+ return 1;
+ case UNAM_NAME:
+ nice_printf(fp, "(%s) ", s);
+ out_addr(fp, ap);
+ nice_printf(fp, ".r");
+ return 1;
+ }
+ case TEXPR:
+ e = (Exprp)np;
+ if (e->opcode == OPWHATSIN)
+ goto tryagain;
+ default:
+ fatali("Unexpected tag %d in opconv_fudge",
+ np->tag);
+ }
+ }
+ nice_printf(fp, "(%s) ", s);
+ }
+ 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 */
+
+ /* prepare to cast procedure parameters -- set A if we know how */
+ np = name->tag == TEXPR && name->exprblock.opcode == OPWHATSIN
+ ? (Namep)name->exprblock.leftp : (Namep)name;
+
+ 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;
+ }
+
+/* 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, "(");
+ expr_out (outfile, name);
+ nice_printf (outfile, ")");
+ }
+ else
+ expr_out(outfile, name);
+
+ 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..93204ab
--- /dev/null
+++ b/usr.bin/f2c/p1output.c
@@ -0,0 +1,723 @@
+/****************************************************************
+Copyright 1990, 1991, 1993, 1994 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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..b6dc75d
--- /dev/null
+++ b/usr.bin/f2c/parse_args.c
@@ -0,0 +1,557 @@
+/****************************************************************
+Copyright 1990, 1994-5 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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*));
+extern int badargs;
+
+
+ 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);
+ badargs++;
+ } /* 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));
+ badargs++;
+ } /* 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);
+ badargs++;
+ } /* 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);
+ badargs++;
+ } /* 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);
+ badargs++;
+ }
+ }
+
+/* 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);
+ badargs++;
+ } /* 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:
+ if (str == NULL) {
+ fprintf(stderr, "%s: Missing argument after '%s%s'\n",
+ this_program, prefix, string);
+ length = 0;
+ badargs++;
+ }
+ else
+ length = strlen(*store = str);
+ 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);
+ badargs++;
+ }
+ 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);
+ badargs++;
+ }
+ 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);
+ badargs++;
+ 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/pread.c b/usr.bin/f2c/pread.c
new file mode 100644
index 0000000..eb1576a
--- /dev/null
+++ b/usr.bin/f2c/pread.c
@@ -0,0 +1,990 @@
+/****************************************************************
+Copyright 1990, 1992, 1993, 1994 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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..6796d52
--- /dev/null
+++ b/usr.bin/f2c/proc.c
@@ -0,0 +1,1821 @@
+/****************************************************************
+Copyright 1990, 1994-6 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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[MAXNAMELEN+4];
+ 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",
+ new_arg_length(np));
+ 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 {
+ if (progname) {
+ /* Construct an empty subroutine with this name */
+ /* in case the name is needed to force loading */
+ /* of this block-data subprogram: the name can */
+ /* appear elsewhere in an external statement. */
+ entrypt(CLPROC, TYSUBR, (ftnint)0, progname, (chainp)0);
+ endproc();
+ newproc();
+ }
+ 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] = (int)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)
+ if (comvar->vleng)
+ size = comvar->vleng->constblock.Const.ci;
+ else {
+ dclerr("character*(*) in common", comvar);
+ size = 1;
+ }
+ 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->vtype != type
+ && (v->vimpltype || v->vinftype || v->vinfproc))
+ {
+ 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;
+ v->vinftype = 0; /* 19960709 */
+ v->vinfproc = 0; /* 19960709 */
+
+ 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 && v->vtype != lengtype(type, length)) {
+ 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;
+ }
+ 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..25425c5
--- /dev/null
+++ b/usr.bin/f2c/put.c
@@ -0,0 +1,441 @@
+/****************************************************************
+Copyright 1990, 1991, 1993, 1994, 1996 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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 */
+ 1,1,1,1,1 /* OPBITTEST, OPBITCLR, OPBITSET, OPQBIT{CLR,SET} */
+};
+
+
+ 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..c104098
--- /dev/null
+++ b/usr.bin/f2c/putpcc.c
@@ -0,0 +1,2075 @@
+/****************************************************************
+Copyright 1990 - 1996 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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));
+
+extern int init_ac[TYSUBR+1];
+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:
+ case OPBITTEST:
+ case OPBITCLR:
+ case OPBITSET:
+#ifdef TYQUAD
+ case OPQBITSET:
+ case OPQBITCLR:
+#endif
+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;
+ char *hsave;
+
+ 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) {
+ if (ISCONST(p->exprblock.leftp)
+ && ISNUMERIC(p->exprblock.vtype)) {
+ hsave = halign;
+ halign = 0;
+ p->exprblock.leftp = putx((expptr)
+ putconst((Constp)
+ p->exprblock.leftp));
+ halign = hsave;
+ }
+ else
+ 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 (ep->entryname && 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};
+
+ 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;
+ }
+ }
+
+ static char*
+#ifdef KR_headers
+get_argtypes(p, pat0, pat1) Exprp p; Argtypes ***pat0, ***pat1;
+#else
+get_argtypes(Exprp p, Argtypes ***pat0, Argtypes ***pat1)
+#endif
+{
+ Addrp a;
+ Argtypes **at0, **at1;
+ Namep np;
+ 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");
+ }
+ *pat0 = at0;
+ *pat1 = at1;
+ return fname;
+ }
+
+ void
+#ifdef KR_headers
+saveargtypes(p)
+ register Exprp p;
+#else
+saveargtypes(register Exprp p)
+#endif
+ /* for writing prototypes */
+{
+ Argtypes **at0, **at1;
+ chainp arglist;
+ expptr rp;
+ char *fname;
+
+ fname = get_argtypes(p, &at0, &at1);
+ rp = p->rightp;
+ arglist = rp && rp->tag == TLIST ? rp->listblock.listp : 0;
+ save_argtypes(arglist, at0, at1, p->opcode == OPCCALL,
+ fname, p->leftp->addrblock.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;
+ Argtypes *at, **at0, **at1;
+ Atype *At, *Ate;
+
+ 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 */
+
+ if (!byvalue) {
+ get_argtypes(p, &at0, &at1);
+ At = Ate = 0;
+ if ((at = *at0) && at->nargs >= 0) {
+ At = at->atypes;
+ Ate = At + at->nargs;
+ At += init_ac[type];
+ }
+ for(cp = arglist ; cp ; cp = cp->nextp) {
+ 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)
+ && (!At || At->type % 100 % TYSUBR == TYCHAR))
+ {
+ 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;
+ }
+ if (At && ++At == Ate)
+ At = 0;
+ }
+ }
+ 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) {
+ if (q->tag == TEXPR && q->exprblock.opcode == OPCONV) {
+ if (ISCOMPLEX(q->exprblock.leftp->headblock.vtype)
+ && q->exprblock.leftp->tag == TEXPR)
+ q->exprblock.leftp = putcxop(q->exprblock.leftp);
+ else
+ q->exprblock.leftp = putx(q->exprblock.leftp);
+ }
+ else
+ cp -> datap = (char *) putx(q);
+ }
+ else if (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/sysdep.c b/usr.bin/f2c/sysdep.c
new file mode 100644
index 0000000..5469034
--- /dev/null
+++ b/usr.bin/f2c/sysdep.c
@@ -0,0 +1,519 @@
+/****************************************************************
+Copyright 1990 - 1994 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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) + 24;
+ 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
+ long pid = getpid();
+ sprintf(c_functions, "%s/f2c%ld_func", tmpdir, pid);
+ sprintf(initfname, "%s/f2c%ld_rd", tmpdir, pid);
+ sprintf(blkdfname, "%s/f2c%ld_blkd", tmpdir, pid);
+ sprintf(p1_file, "%s/f2c%ld_p1f", tmpdir, pid);
+ sprintf(p1_bakfile, "%s/f2c%ld_p1fb", tmpdir, pid);
+ sprintf(sortfname, "%s/f2c%ld_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..e3a68ef
--- /dev/null
+++ b/usr.bin/f2c/sysdep.h
@@ -0,0 +1,98 @@
+/****************************************************************
+Copyright 1990, 1991, 1994 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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 Bits_per_Byte 8
+#define Table_size (1 << Bits_per_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..fa78805
--- /dev/null
+++ b/usr.bin/f2c/vax.c
@@ -0,0 +1,570 @@
+/****************************************************************
+Copyright 1990, 1992, 1993, 1994 by AT&T, Lucent Technologies 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,
+Lucent 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, Lucent and Bellcore disclaim all warranties with regard to
+this software, including all implied warranties of
+merchantability and fitness. In no event shall AT&T, Lucent 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;
+ expptr e1;
+
+ if (e != ENULL)
+ switch (e -> tag) {
+ case TADDR:
+ if (e->addrblock.isarray) {
+ if (e1 = e->addrblock.memoffset)
+ e->addrblock.memoffset = make_int_expr(e1);
+ }
+ else if (e->addrblock.vstg == STGARG)
+ 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..87a0922
--- /dev/null
+++ b/usr.bin/f2c/version.c
@@ -0,0 +1,2 @@
+char F2C_version[] = "19970219";
+char xxxvers[] = "\n@(#) FORTRAN 77 to C Translator, VERSION 19970219\n";
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/fetch/Makefile b/usr.bin/fetch/Makefile
new file mode 100644
index 0000000..31479bd
--- /dev/null
+++ b/usr.bin/fetch/Makefile
@@ -0,0 +1,9 @@
+PROG = fetch
+SRCS = file.c ftp.c http.c main.c util.c uri.c
+
+CFLAGS+= -Wall -Wwrite-strings -Wmissing-prototypes
+
+DPADD= ${LIBFTPIO} ${LIBMD}
+LDADD= -lftpio -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/fetch/fetch.1 b/usr.bin/fetch/fetch.1
new file mode 100644
index 0000000..924e867
--- /dev/null
+++ b/usr.bin/fetch/fetch.1
@@ -0,0 +1,303 @@
+.\" $Id: fetch.1,v 1.16 1997/02/22 23:43:32 wosch Exp $
+.Dd July 2, 1996
+.Dt FETCH 1
+.Os FreeBSD 2.2
+.Sh NAME
+.Nm fetch
+.Nd retrieve a file by Uniform Resource Locator
+.Sh SYNOPSIS
+.Nm fetch
+.Op Fl MPamnpqr
+.Op Fl o Ar file
+.Ar URL
+.Op Ar ...
+.Nm fetch
+.Op Fl MPRmnpqr
+.Op Fl o Ar file
+.Op Fl c Ar dir
+.Fl f Ar file
+.Fl h Ar host
+.Sh DESCRIPTION
+.Nm fetch
+allows a user to transfer files from a remote network site using
+either the
+.Tn FTP
+or the
+.Tn HTTP
+protocol. In the first form of the command, the
+.Ar URL
+may be of the form
+.Li http://site.domain/path/to/the/file
+or
+.Li ftp://site.domain/path/to/the/file.
+To denote a local filename to be copied or linked to (see the
+.Fl l
+flag below), the
+.Em file:/path/to/the/file
+URL form is used.
+.Pp
+The second form of the command can be used to get a file using the
+.Tn FTP
+protocol, specifying the file name and the remote host with the
+.Fl h
+and the
+.Fl f
+flags.
+.Pp
+The following options are available:
+.Bl -tag -width Fl
+.It Fl a
+Automatically retry the transfer upon soft failures.
+.It Fl c Ar dir
+The file to retrieve is in directory
+.Ar dir
+on the remote host.
+.It Fl f Ar file
+The file to retrieve is named
+.Ar file
+on the remote host.
+.It Fl h Ar host
+The file to retrieve is located on the host
+.Ar host .
+.It Fl l
+If target is a
+.Ar file:/
+style of URL, make a link to the target rather than trying
+to copy it.
+.It Fl M
+.It Fl m
+Mirror mode: Set the modification time of the file so that it is
+identical to the modification time of the file at the remote host.
+If the file already exists on the local host and is identical (as
+gauged by size and modification time), no transfer is done.
+.It Fl n
+Don't preserve the modtime of the transfered file, use the current time.
+.It Fl o Ar file
+Set the output file name to
+.Ar file .
+By default, a ``pathname'' is extracted from the specified URI, and
+its basename is used as the name of the output file. A
+.Ar file
+argument of
+.Sq Li \&-
+indicates that results are to be directed to the standard output.
+.It Fl P
+.It Fl p
+Use the passive mode of the
+.Tn FTP
+protocol. This is useful for crossing certain sorts of firewalls.
+.It Fl q
+Quiet mode. Do not report transfer progress on the terminal.
+.It Fl R
+The filenames specified are ``precious'', and should not be deleted
+under any circumstances, even if the transfer failed or was incomplete.
+.It Fl r
+Restart a previously interrupted transfer.
+.It Fl T Ar seconds
+Set timeout value to
+.Ar seconds.
+Overrides the environment variables
+.Ev FTP_TIMEOUT
+for ftp transfers or
+.Ev HTTP_TIMEOUT
+for http transfers if set.
+.It Fl v
+Increase verbosity. More
+.Fl v Ns \&'s
+result in more information.
+.El
+.Pp
+Many options are also controlled solely by the environment (this is a
+bug).
+.Sh PROXY SERVERS
+Many sites use application gateways (``proxy servers'') in their
+firewalls in order to allow communication across the firewall using a
+trusted protocol. The
+.Nm fetch
+program can use both the
+.Tn FTP
+and the
+.Tn HTTP
+protocol with a proxy server.
+.Tn FTP
+proxy servers can only relay
+.Tn FTP
+requests;
+.Tn HTTP
+proxy servers can relay both
+.Tn FTP
+and
+.Tn HTTP
+requests.
+A proxy server can be configured by defining an environment variable
+named
+.Dq Va PROTO Ns Ev _PROXY ,
+where
+.Va PROTO
+is the name of the protocol in upper case. The value of the
+environment variable specifies a hostname, optionally followed by a
+colon and a port number.
+.Pp
+The
+.Tn FTP
+proxy client passes the remote username, host and port as the
+.Tn FTP
+session's username, in the form
+.Do Va remoteuser Ns Li \&@ Ns Va remotehost
+.Op Li \^@ Ns Va port
+.Dc .
+The
+.Tn HTTP
+proxy client simply passes the originally-requested URI to the remote
+server in an
+.Tn HTTP
+.Dq Li GET
+request. HTTP proxy authentication is not yet implemented.
+.Sh HTTP AUTHENTICATION
+The
+.Tn HTTP
+protocol includes support for various methods of authentication.
+Currently, the
+.Dq basic
+method, which provides no security from packet-sniffing or
+man-in-the-middle attacks, is the only method supported in
+.Nm fetch .
+Authentication is enabled by the
+.Ev HTTP_AUTH
+and
+.Ev HTTP_PROXY_AUTH
+environment variables. Both variables have the same format, which
+consists of space-separated list of parameter settings, where each
+setting consists of a colon-separated list of parameters. The first
+two parameters are always the (case-insensitive) authentication scheme
+name and the realm in which authentication is to be performed. If the
+realm is specified as
+.Sq Li \&* ,
+then it will match all realms not specified otherwise.
+.Pp
+For the
+.Li basic
+authentication scheme uses two additional optional parameters; the
+first is a user name, and the second is the password associated with
+it. If either the password or both parameters are not specified in
+the environment, and the standard input of
+.Nm
+is connected to a terminal, then
+.Nm
+will prompt the user to enter the missing parameters. Thus, if the
+user is known as
+.Dq Li jane
+in the
+.Dq Li WallyWorld
+realm, and has a password of
+.Dq Li QghiLx79
+there, then she might set her
+.Ev HTTP_AUTH
+variable to:
+.Bl -enum -offset indent
+.It
+.Dq Li basic:WallyWorld:jane:QghiLx79
+.It
+.Dq Li basic:WallyWorld:jane ,
+or
+.It
+.Dq Li basic:WallyWorld
+.El
+.Pp
+and
+.Nm
+will prompt for the missing information if it is required. She might
+also specify a realm of
+.Dq Li \&*
+instead of
+.Dq Li WallyWorld
+to indicate that the parameters can be applied to any realm. (This is
+most commonly used in a construction such as
+.Dq Li basic:* ,
+which indicates to
+.Nm
+that it may offer to do
+.Li basic
+authentication for any realm.
+.Sh ERRORS
+The
+.Nm
+command returns zero on success, or a non-zero value from
+.Aq Pa sysexits.h
+on failure. If multiple URIs are given for retrieval,
+.Nm
+will attempt all of them and return zero only if all succeeded
+(otherwise it will return the error from the last failure).
+.Sh ENVIRONMENT
+.Bl -tag -width FTP_PASSIVE_MODE -offset indent
+.It Ev FTP_TIMEOUT
+maximum time, in seconds, to wait before aborting an
+.Tn FTP
+connection.
+.It Ev FTP_LOGIN
+the login name used for
+.Tn FTP
+transfers (default
+.Dq Li anonymous )
+.It Ev FTP_PASSIVE_MODE
+force the use of passive mode FTP
+.It Ev FTP_PASSWORD
+the password used for
+.Tn FTP
+transfers (default
+.Dq Va yourname Ns Li \&@ Ns Va yourhost )
+.It Ev FTP_PROXY
+the address (in the form
+.Do Va hostname Ns
+.Op Li : Ns Va port
+.Dc )
+of a proxy server which understands
+.Tn FTP
+.It Ev HTTP_AUTH
+defines authentication parameters for
+.Tn HTTP
+.It Ev HTTP_PROXY
+the address (in the form
+.Do Va hostname Ns
+.Op Li : Ns Va port
+.Dc )
+of a proxy server which understands
+.Tn HTTP
+.It Ev HTTP_PROXY_AUTH
+defines authentication parameters for
+.Tn HTTP
+proxy servers
+.It Ev HTTP_TIMEOUT
+maximum time, in seconds, to wait before aborting an
+.Tn HTTP
+connection.
+.Sh SEE ALSO
+.Xr ftp 1 ,
+.Xr tftp 1
+.Sh HISTORY
+The
+.Nm fetch
+command appeared in
+.Fx 2.1.5 .
+.Sh AUTHORS
+The original implementation of
+.Nm
+was done by Jean-Marc Zucconi. It was extensively re-worked for
+.Fx 2.2
+by Garrett Wollman.
+.Sh BUGS
+There are too many environment variables and command-line options.
+.Pp
+The
+.Fl a
+option is only implemented for certain kinds of
+.Tn HTTP
+failures, and no
+.Tn FTP
+failures.
+.Pp
+Only the
+.Dq basic
+authentication mode is implemented for
+.Tn HTTP .
+This should be replaced by digest authentication.
diff --git a/usr.bin/fetch/fetch.h b/usr.bin/fetch/fetch.h
new file mode 100644
index 0000000..321af11
--- /dev/null
+++ b/usr.bin/fetch/fetch.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 1997 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: fetch.h,v 1.2 1997/01/31 19:55:49 wollman Exp $
+ */
+
+#ifndef fetch_h
+#define fetch_h 1
+
+
+#define BUFFER_SIZE 1024
+#define FETCH_VERSION "fetch/1.0"
+#define PATH_CP "/bin/cp"
+
+struct fetch_state {
+ const char *fs_status;
+ const char *fs_outputfile;
+ int fs_verbose; /* -q, -v option */
+ int fs_newtime; /* -n option */
+ int fs_mirror; /* -m option */
+ int fs_restart; /* -r option */
+ int fs_timeout; /* -T option */
+ int fs_passive_mode; /* -p option */
+ int fs_linkfile; /* -l option */
+ int fs_precious; /* -R option */
+ int fs_auto_retry; /* -a option */
+ time_t fs_modtime;
+ void *fs_proto;
+ int (*fs_retrieve)(struct fetch_state *);
+ int (*fs_close)(struct fetch_state *);
+};
+
+struct uri_scheme {
+ const char *sc_name; /* name of the scheme, <32 characters */
+ int (*sc_parse)(struct fetch_state *, const char *);
+ /* routine to parse a URI and build state */
+ int (*sc_proxy_parse)(struct fetch_state *, const char *);
+ /* same, but for proxy case */
+ const char *sc_proxy_envar; /* envar used to determine proxy */
+ const char *sc_proxy_by; /* list of protos which can proxy us */
+
+ /* The rest is filled in dynamically... */
+ int sc_can_proxy;
+ struct uri_scheme *sc_proxyproto;
+};
+
+extern struct uri_scheme file_scheme, ftp_scheme, http_scheme;
+
+void adjmodtime(struct fetch_state *fs);
+void catchsig(int signo);
+void display(struct fetch_state *fs, off_t total, ssize_t thisincr);
+void init_schemes(void);
+void rm(struct fetch_state *fs);
+void setup_sigalrm(void);
+void unsetup_sigalrm(void);
+void *safe_malloc(size_t len);
+char *percent_decode(const char *orig);
+char *safe_strdup(const char *orig);
+char *safe_strndup(const char *orig, size_t len);
+char *to_base64(const unsigned char *buf, size_t len);
+int from_base64(const char *orig, unsigned char *buf, size_t *lenp);
+int parse_host_port(const char *str, char **hostname, int *port);
+int parse_uri(struct fetch_state *fs, const char *uri);
+#endif /* ! fetch_h */
diff --git a/usr.bin/fetch/file.c b/usr.bin/fetch/file.c
new file mode 100644
index 0000000..091639c
--- /dev/null
+++ b/usr.bin/fetch/file.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright 1997 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$
+ */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+
+#include "fetch.h"
+
+static int file_retrieve(struct fetch_state *fs);
+static int file_close(struct fetch_state *fs);
+static int file_parse(struct fetch_state *fs, const char *uri);
+
+struct uri_scheme file_scheme =
+ { "file", file_parse, 0, 0, 0 };
+
+/*
+ * Again, we slightly misinterpret the slash after the hostname as
+ * being the start of the pathname rather than merely a separator.
+ */
+static int
+file_parse(struct fetch_state *fs, const char *uri)
+{
+ const char *p;
+
+ p = uri + 5; /* skip past `file:' */
+ if (p[0] == '/' && p[1] == '/') {
+ /* skip past `//localhost', if any */
+ p += 2;
+ while (*p && *p != '/')
+ p++;
+ }
+
+ if (p[0] != '/') {
+ warnx("`%s': expected absolute pathname in `file' URL", uri);
+ return EX_USAGE;
+ }
+
+ fs->fs_proto = percent_decode(p);
+ /* guaranteed to succeed because of above test */
+ p = strrchr(fs->fs_proto, '/');
+ if (fs->fs_outputfile == 0) /* only set if not overridden by user */
+ fs->fs_outputfile = p + 1;
+ fs->fs_retrieve = file_retrieve;
+ fs->fs_close = file_close;
+ return 0;
+}
+
+static int
+file_close(struct fetch_state *fs)
+{
+ free(fs->fs_proto);
+ fs->fs_proto = 0;
+ fs->fs_outputfile = 0;
+ fs->fs_status = "free";
+ return 0;
+}
+
+static int
+file_retrieve(struct fetch_state *fs)
+{
+ /* XXX - this seems bogus to me! */
+ if (access(fs->fs_outputfile, F_OK) == 0) {
+ errno = EEXIST;
+ warn("%s", fs->fs_outputfile);
+ return EX_USAGE;
+ }
+
+ if (fs->fs_linkfile) {
+ fs->fs_status = "symlink";
+ if (symlink(fs->fs_proto, fs->fs_outputfile) == -1) {
+ warn("symlink");
+ return EX_OSERR;
+ }
+ fs->fs_status = "done";
+ } else {
+ pid_t pid;
+ int status;
+
+ fflush(stderr);
+ pid = fork();
+ if (pid < 0) {
+ warn("fork");
+ return EX_TEMPFAIL;
+ } else if (pid == 0) {
+ execl(PATH_CP, "cp", "-p", fs->fs_proto,
+ fs->fs_outputfile, (char *)0);
+ warn("execl: " PATH_CP);
+ fflush(stderr);
+ _exit(EX_OSERR);
+ } else {
+ fs->fs_status = "copying";
+ if (waitpid(pid, &status, 0) < 0) {
+ warn("waitpid(%ld)", (long)pid);
+ return EX_OSERR;
+ }
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ if (WIFSIGNALED(status))
+ warn(PATH_CP " exited on signal: %s",
+ sys_signame[WTERMSIG(status)]);
+ return EX_OSERR;
+ }
+ }
+ return 0;
+}
+
diff --git a/usr.bin/fetch/ftp.c b/usr.bin/fetch/ftp.c
new file mode 100644
index 0000000..457e43d
--- /dev/null
+++ b/usr.bin/fetch/ftp.c
@@ -0,0 +1,427 @@
+/*-
+ * Copyright 1997 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: ftp.c,v 1.6 1997/03/11 15:13:28 jmg Exp $
+ */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <ftpio.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "fetch.h"
+
+struct ftp_state {
+ char *ftp_hostname;
+ char *ftp_user;
+ char *ftp_password;
+ char *ftp_remote_file;
+ unsigned ftp_port;
+};
+
+static int ftp_close(struct fetch_state *fs);
+static int ftp_retrieve(struct fetch_state *fs);
+static int ftp_parse(struct fetch_state *fs, const char *uri);
+static int ftp_proxy_parse(struct fetch_state *fs, const char *uri);
+
+struct uri_scheme ftp_scheme =
+ { "ftp", ftp_parse, ftp_proxy_parse, "FTP_PROXY", "ftp,http" };
+
+static int
+ftp_parse(struct fetch_state *fs, const char *uri)
+{
+ const char *p, *slash, *q;
+ char *hostname, *atsign, *colon;
+ unsigned port;
+ struct ftp_state *ftps;
+
+ p = uri + 4;
+ port = 0;
+
+ if (p[0] != '/' || p[1] != '/') {
+ warnx("`%s': invalid `ftp' URL", uri);
+ return EX_USAGE;
+ }
+
+ p += 2;
+ slash = strchr(p, '/');
+ if (slash == 0) {
+ warnx("`%s': malformed `ftp' URL", uri);
+ return EX_USAGE;
+ }
+ hostname = alloca(slash - p + 1);
+ hostname[0] = '\0';
+ strncat(hostname, p, slash - p);
+
+ if ((atsign = strrchr(hostname, '@')) == 0)
+ q = hostname;
+ else
+ q = atsign + 1;
+
+ if ((colon = strchr(q, ':')) != 0)
+ *colon = '\0';
+
+ if (colon && *(colon + 1)) {
+ unsigned long ul;
+ char *ep;
+
+ errno = 0;
+ ul = strtoul(colon + 1, &ep, 10);
+ if (*ep || errno != 0 || ul < 1 || ul > 65534) {
+ if (errno)
+ warn("`%s': invalid port in URL", uri);
+ else
+ warnx("`%s': invalid port in URL", uri);
+ return EX_USAGE;
+ }
+
+ port = ul;
+ } else {
+ port = 21;
+ }
+
+ p = slash + 1;
+
+ ftps = safe_malloc(sizeof *ftps);
+ ftps->ftp_password = 0;
+ ftps->ftp_user = 0;
+
+ /*
+ * Now, we have a copy of the hostname in hostname, the specified port
+ * (or the default value) in port, and p points to the filename part
+ * of the URI. We just need to check for a user in the hostname,
+ * and then save all the bits in our state.
+ */
+ if (atsign) {
+ if (atsign[1] == '\0') {
+ warnx("`%s': malformed `ftp' hostname", hostname);
+ free(ftps);
+ return EX_USAGE;
+ }
+
+ *atsign = '\0';
+ if ((colon = strchr(hostname, ':')) != 0)
+ *colon = '\0';
+ if (hostname[0] == '\0') {
+ warnx("`%s': malformed `ftp' user", atsign + 1);
+ free(ftps);
+ return EX_USAGE;
+ }
+ if (colon != 0)
+ ftps->ftp_password = percent_decode(colon + 1);
+ ftps->ftp_user = percent_decode(hostname);
+ ftps->ftp_hostname = safe_strdup(atsign + 1);
+ } else
+ ftps->ftp_hostname = safe_strdup(hostname);
+ ftps->ftp_port = port;
+
+ p = ftps->ftp_remote_file = percent_decode(p);
+ /* now p is the decoded version */
+
+ if (fs->fs_outputfile == 0) {
+ slash = strrchr(p, '/');
+ fs->fs_outputfile = slash ? slash + 1 : p;
+ }
+
+ if (ftps->ftp_password == 0)
+ ftps->ftp_password = getenv("FTP_PASSWORD");
+ if (ftps->ftp_password != 0) {
+ ftps->ftp_password = safe_strdup(ftps->ftp_password);
+ } else {
+ char *pw;
+ const char *logname;
+ char localhost[MAXHOSTNAMELEN];
+
+ logname = getlogin();
+ if (logname == 0)
+ logname = "root";
+ gethostname(localhost, sizeof localhost);
+ pw = safe_malloc(strlen(logname) + 1 + strlen(localhost) + 1);
+ strcpy(pw, logname);
+ strcat(pw, "@");
+ strcat(pw, localhost);
+ ftps->ftp_password = pw;
+ setenv("FTP_PASSWORD", pw, 0); /* cache the result */
+ }
+
+ if (ftps->ftp_user == 0)
+ ftps->ftp_user = getenv("FTP_LOGIN");
+ if (ftps->ftp_user != 0)
+ ftps->ftp_user = safe_strdup(ftps->ftp_user);
+
+ fs->fs_proto = ftps;
+ fs->fs_close = ftp_close;
+ fs->fs_retrieve = ftp_retrieve;
+ return 0;
+}
+
+/*
+ * The only URIs we can handle in the FTP proxy are FTP URLs.
+ * This makes it possible to take a few short cuts.
+ */
+static int
+ftp_proxy_parse(struct fetch_state *fs, const char *uri)
+{
+ int rv;
+ char *hostname;
+ char *port;
+ const char *user;
+ char *newuser;
+ unsigned portno;
+ struct ftp_state *ftps;
+
+ hostname = getenv("FTP_PROXY");
+ port = strchr(hostname, ':');
+ if (port == 0) {
+ portno = 21;
+ } else {
+ unsigned long ul;
+ char *ep;
+
+ /* All this to avoid modifying the environment. */
+ ep = alloca(strlen(hostname) + 1);
+ strcpy(ep, hostname);
+ port = ep + (port - hostname);
+ hostname = ep;
+
+ *port++ = '\0';
+ errno = 0;
+ ul = strtoul(port, &ep, 0);
+ if (*ep || !*port || errno != 0 || ul < 1 || ul > 65534) {
+ warnx("`%s': invalid port specification for FTP proxy",
+ port);
+ return EX_USAGE;
+ }
+ portno = ul;
+ }
+
+ /* ftp_parse() does most of the work; we can just fix things up */
+ rv = ftp_parse(fs, uri);
+ if (rv)
+ return rv;
+ /* Oops.. it got turned into a file: */
+ if (fs->fs_retrieve != ftp_retrieve) {
+ return 0;
+ }
+
+ ftps = fs->fs_proto;
+
+ user = ftps->ftp_user ? ftps->ftp_user : "anonymous";
+ /* user @ hostname [ @port ] \0 */
+ newuser = safe_malloc(strlen(user) + 1 + strlen(ftps->ftp_hostname)
+ + ((ftps->ftp_port != 21) ? 6 : 0) + 1);
+
+ strcpy(newuser, user);
+ strcat(newuser, "@");
+ strcat(newuser, ftps->ftp_hostname);
+ if (ftps->ftp_port != 21) {
+ char numbuf[6];
+
+ snprintf(numbuf, sizeof(numbuf), "%d", ftps->ftp_port);
+ numbuf[sizeof(numbuf)-1] = '\0';
+ strcat(newuser, "@");
+ strcat(newuser, numbuf);
+ }
+
+ ftps->ftp_port = portno;
+ free(ftps->ftp_hostname);
+ ftps->ftp_hostname = safe_strdup(hostname);
+ free(ftps->ftp_user);
+ ftps->ftp_user = newuser;
+ return 0;
+}
+
+static int
+ftp_close(struct fetch_state *fs)
+{
+ struct ftp_state *ftps = fs->fs_proto;
+
+ if (ftps->ftp_user)
+ free(ftps->ftp_user);
+ free(ftps->ftp_hostname);
+ free(ftps->ftp_password);
+ free(ftps->ftp_remote_file);
+ free(ftps);
+ fs->fs_proto = 0;
+ fs->fs_outputfile = 0;
+ return 0;
+}
+
+static int
+ftp_retrieve(struct fetch_state *fs)
+{
+ struct ftp_state *ftps = fs->fs_proto;
+ FILE *ftp, *remote, *local;
+ int status;
+ off_t size;
+ off_t seekloc, wehave;
+ time_t modtime;
+ size_t readresult, writeresult;
+
+ ftp = ftpLogin(ftps->ftp_hostname,
+ (char *)(ftps->ftp_user ? ftps->ftp_user : "anonymous"),
+ /* XXX ^^^^ bad API */
+ ftps->ftp_password, ftps->ftp_port, fs->fs_verbose > 1,
+ &status);
+ if (ftp == 0) {
+ warnx("%s: %s", ftps->ftp_hostname,
+ status ? ftpErrString(status) : hstrerror(h_errno));
+ return EX_IOERR;
+ }
+ ftpBinary(ftp);
+ ftpPassive(ftp, fs->fs_passive_mode);
+ size = ftpGetSize(ftp, ftps->ftp_remote_file);
+ modtime = ftpGetModtime(ftp, ftps->ftp_remote_file);
+ if (modtime <= 0) { /* xxx */
+ warnx("%s: cannot get remote modification time",
+ ftps->ftp_remote_file);
+ modtime = -1;
+ }
+ fs->fs_modtime = modtime;
+ seekloc = wehave = 0;
+ if (fs->fs_restart || fs->fs_mirror) {
+ struct stat stab;
+
+ if (fs->fs_outputfile[0] == '-'
+ && fs->fs_outputfile[1] == '\0')
+ status = fstat(STDOUT_FILENO, &stab);
+ else
+ status = stat(fs->fs_outputfile, &stab);
+ if (status < 0) {
+ stab.st_mtime = -1;
+ stab.st_size = 0;
+ }
+ if (status == 0 && !S_ISREG(stab.st_mode)) {
+ fs->fs_restart = 0;
+ fs->fs_mirror = 0;
+ }
+ if (fs->fs_mirror && stab.st_size == size
+ && modtime <= stab.st_mtime) {
+ fclose(ftp);
+ return 0;
+ }
+ if (fs->fs_restart) {
+ if (stab.st_size != 0 && stab.st_size < size)
+ seekloc = wehave = stab.st_size;
+ }
+ }
+
+ remote = ftpGet(ftp, ftps->ftp_remote_file, &seekloc);
+ if (remote == 0) {
+ if (ftpErrno(ftp)) {
+ warnx("ftp://%s/%s: FTP error:",
+ ftps->ftp_hostname, ftps->ftp_remote_file);
+ warnx("%s", ftpErrString(ftpErrno(ftp)));
+ fclose(ftp);
+ return EX_IOERR;
+ } else {
+ warn("ftpGet");
+ return EX_OSERR;
+ }
+ }
+
+ if (fs->fs_outputfile[0] == '-' && fs->fs_outputfile[1] == '\0')
+ local = fopen("/dev/stdout", wehave ? "a" : "w");
+ else
+ local = fopen(fs->fs_outputfile, wehave ? "a" : "w");
+ if (local == 0) {
+ warn("%s", fs->fs_outputfile);
+ fclose(remote);
+ fclose(ftp);
+ return EX_OSERR;
+ }
+
+ if (fs->fs_timeout) {
+ char buf[sizeof("18446744073709551616")]; /* 2**64 */
+ snprintf(buf, sizeof buf, "%d", fs->fs_timeout);
+ setenv("FTP_TIMEOUT", buf, 1);
+ } else {
+ char *env = getenv("FTP_TIMEOUT");
+ char *ep;
+ unsigned long ul;
+
+ if (env) {
+ errno = 0;
+ ul = strtoul(env, &ep, 0);
+ if (*env && *ep == '\0' && errno == 0 && ul <= INT_MAX)
+ fs->fs_timeout = ul;
+ else
+ warnx("`%s': invalid FTP timeout", env);
+ }
+ }
+
+ display(fs, size, wehave);
+ setup_sigalrm();
+
+ do {
+ char buf[BUFFER_SIZE];
+
+ alarm(fs->fs_timeout);
+ readresult = fread(buf, 1, sizeof buf, remote);
+ alarm(0);
+ if (readresult == 0)
+ break;
+ display(fs, size, readresult);
+ writeresult = fwrite(buf, 1, readresult, local);
+ } while (writeresult == readresult);
+ unsetup_sigalrm();
+
+ if (ferror(remote)) {
+ warn("reading remote file from %s", ftps->ftp_hostname);
+ fclose(local);
+ fclose(remote);
+ fclose(ftp);
+ rm(fs);
+ return EX_IOERR;
+ } else if(ferror(local)) {
+ warn("%s", fs->fs_outputfile);
+ fclose(local);
+ fclose(remote);
+ fclose(ftp);
+ rm(fs);
+ return EX_IOERR;
+ }
+
+ fclose(local);
+ fclose(remote);
+ fclose(ftp);
+ display(fs, size, -1);
+ adjmodtime(fs);
+ return 0;
+}
diff --git a/usr.bin/fetch/http.c b/usr.bin/fetch/http.c
new file mode 100644
index 0000000..fa445e0
--- /dev/null
+++ b/usr.bin/fetch/http.c
@@ -0,0 +1,1605 @@
+/*-
+ * Copyright 1997 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: http.c,v 1.5 1997/03/05 18:57:16 fenner Exp $
+ */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <md5.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/param.h> /* for MAXHOSTNAMELEN */
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "fetch.h"
+
+struct http_state {
+ char *http_hostname;
+ char *http_remote_request;
+ char *http_decoded_file;
+ char *http_host_header;
+ char *http_authentication;
+ char *http_proxy_authentication;
+ unsigned http_port;
+ int http_redirected;
+};
+
+struct http_auth {
+ TAILQ_ENTRY(http_auth) ha_link;
+ char *ha_scheme;
+ char *ha_realm;
+ char *ha_params;
+ const struct http_auth_method *ha_ham;
+};
+TAILQ_HEAD(http_auth_head, http_auth);
+
+static int http_parse(struct fetch_state *fs, const char *uri);
+static int http_proxy_parse(struct fetch_state *fs, const char *uri);
+static int http_close(struct fetch_state *fs);
+static int http_retrieve(struct fetch_state *fs);
+static int basic_doauth(struct fetch_state *fs, struct http_auth *ha, int prx);
+
+struct uri_scheme http_scheme =
+ { "http", http_parse, http_proxy_parse, "HTTP_PROXY", "http" };
+
+struct http_auth_head http_auth, http_proxy_auth;
+
+struct http_auth_method {
+ const char *ham_scheme;
+ int (*ham_doauth)(struct fetch_state *, struct http_auth *, int);
+} http_auth_methods[] = {
+ { "basic", basic_doauth },
+ { 0, 0 }
+};
+
+/* We are only concerned with headers we might receive. */
+enum http_header {
+ ht_accept_ranges, ht_age, ht_allow, ht_cache_control, ht_connection,
+ ht_content_base, ht_content_encoding, ht_content_language,
+ ht_content_length, ht_content_location, ht_content_md5,
+ ht_content_range, ht_content_type, ht_date, ht_etag, ht_expires,
+ ht_last_modified, ht_location, ht_pragma, ht_proxy_authenticate,
+ ht_public, ht_retry_after, ht_server, ht_transfer_encoding,
+ ht_upgrade, ht_vary, ht_via, ht_www_authenticate, ht_warning,
+ /* unusual cases */
+ ht_syntax_error, ht_unknown, ht_end_of_header
+};
+
+static char *format_http_date(time_t when);
+static char *format_http_user_agent(void);
+static enum http_header http_parse_header(char *line, char **valuep);
+static int check_md5(FILE *fp, char *base64ofmd5);
+static int http_first_line(const char *line);
+static int parse_http_content_range(char *orig, off_t *first, off_t *total);
+static int process_http_auth(struct fetch_state *fs, char *hdr, int autherr);
+static struct http_auth *find_http_auth(struct http_auth_head *list,
+ const char *scheme, const char *realm);
+static time_t parse_http_date(char *datestring);
+static void setup_http_auth(void);
+
+static int
+http_parse(struct fetch_state *fs, const char *uri)
+{
+ const char *p, *colon, *slash, *ques, *q;
+ char *hostname, *hosthdr, *trimmed_name;
+ unsigned port;
+ struct http_state *https;
+
+ p = uri + 5;
+ port = 0;
+
+ if (p[0] != '/' || p[1] != '/') {
+ warnx("`%s': malformed `http' URL", uri);
+ return EX_USAGE;
+ }
+
+ p += 2;
+ colon = strchr(p, ':');
+ slash = strchr(p, '/');
+ if (colon && slash && colon < slash)
+ q = colon;
+ else
+ q = slash;
+ if (q == 0) {
+ warnx("`%s': malformed `http' URL", uri);
+ return EX_USAGE;
+ }
+ hostname = alloca(q - p + 1);
+ hostname[0] = '\0';
+ strncat(hostname, p, q - p);
+ p = slash;
+
+ if (colon && colon + 1 != slash) {
+ unsigned long ul;
+ char *ep;
+
+ errno = 0;
+ ul = strtoul(colon + 1, &ep, 10);
+ if (ep != slash || ep == colon + 1 || errno != 0
+ || ul < 1 || ul > 65534) {
+ warn("`%s': invalid port in URL", uri);
+ return EX_USAGE;
+ }
+
+ port = ul;
+ } else {
+ port = 80;
+ }
+
+ p = slash;
+
+ https = safe_malloc(sizeof *https);
+
+ /*
+ * Now, we have a copy of the hostname in hostname, the specified port
+ * (or the default value) in port, and p points to the filename part
+ * of the URI.
+ */
+ https->http_hostname = safe_strdup(hostname);
+ https->http_port = port;
+ hosthdr = alloca(sizeof("Host: :\r\n") + 5 + strlen(hostname));
+ sprintf(hosthdr, "Host: %s:%d\r\n", hostname, port);
+ https->http_host_header = safe_strdup(hosthdr);
+
+ /*
+ * NB: HTTP/1.1 servers MUST also accept a full URI.
+ * However, HTTP/1.0 servers will ONLY accept a trimmed URI.
+ */
+ https->http_remote_request = safe_strdup(p);
+ p++;
+ ques = strpbrk(p, "?#");
+ if (ques) {
+ trimmed_name = safe_strndup(p, ques - p);
+ } else {
+ trimmed_name = safe_strdup(p);
+ }
+ https->http_decoded_file = percent_decode(trimmed_name);
+ free(trimmed_name);
+ p = https->http_decoded_file;
+ /* now p is the decoded version, so we can extract the basename */
+
+ if (fs->fs_outputfile == 0) {
+ slash = strrchr(p, '/');
+ if (slash)
+ fs->fs_outputfile = slash + 1;
+ else
+ fs->fs_outputfile = p;
+ }
+ https->http_redirected = 0;
+ https->http_authentication = https->http_proxy_authentication = 0;
+
+ fs->fs_proto = https;
+ fs->fs_close = http_close;
+ fs->fs_retrieve = http_retrieve;
+ return 0;
+}
+
+/*
+ * An HTTP proxy works by accepting a complete URI in a GET request,
+ * retrieving that object, and then forwarding it back to us. Because
+ * it can conceivably handle any URI, we have to do a bit more work
+ * in the parsing of it.
+ */
+static int
+http_proxy_parse(struct fetch_state *fs, const char *uri)
+{
+ struct http_state *https;
+ const char *env, *slash, *ques;
+ char *file;
+ int rv;
+
+ https = safe_malloc(sizeof *https);
+ https->http_remote_request = safe_strdup(uri);
+
+ env = getenv("HTTP_PROXY");
+ rv = parse_host_port(env, &https->http_hostname, &https->http_port);
+ if (rv) {
+out:
+ free(https->http_remote_request);
+ free(https);
+ return rv;
+ }
+
+ if (strncmp(uri, "http://", 7) == 0) {
+ char *hosthdr;
+ slash = strchr(uri + 7, '/');
+ if (slash == 0) {
+ warnx("`%s': malformed `http' URL", uri);
+ rv = EX_USAGE;
+ free(https->http_hostname);
+ goto out;
+ }
+ ques = strpbrk(slash, "?#");
+ if (ques == 0)
+ file = safe_strdup(slash);
+ else
+ file = safe_strndup(slash, ques - slash);
+ hosthdr = alloca(sizeof("Host: \r\n") + slash - uri - 7);
+ strcpy(hosthdr, "Host: ");
+ strncat(hosthdr, uri + 7, slash - uri - 7);
+ strcat(hosthdr, "\r\n");
+ https->http_host_header = safe_strdup(hosthdr);
+ } else {
+ slash = uri;
+ while (*slash && *slash != ':')
+ slash++;
+ if (*slash)
+ slash++;
+ if (slash[0] == '/' && slash[1] == '/') {
+ slash += 2;
+ while (*slash && *slash != '/')
+ slash++;
+ }
+ file = safe_strdup(slash);
+ https->http_host_header = safe_strdup("");
+ }
+ https->http_decoded_file = percent_decode(file);
+ https->http_redirected = 0;
+ https->http_authentication = https->http_proxy_authentication = 0;
+ free(file);
+ if (fs->fs_outputfile == 0) {
+ slash = strrchr(https->http_decoded_file, '/');
+ /* NB: we are not guaranteed to find one... */
+ fs->fs_outputfile = slash ? slash + 1
+ : https->http_decoded_file;
+ }
+
+ fs->fs_proto = https;
+ fs->fs_close = http_close;
+ fs->fs_retrieve = http_retrieve;
+ return 0;
+}
+
+static int
+http_close(struct fetch_state *fs)
+{
+ struct http_state *https = fs->fs_proto;
+
+ free(https->http_hostname);
+ free(https->http_remote_request);
+ free(https->http_decoded_file);
+ free(https->http_host_header);
+ if (https->http_authentication)
+ free(https->http_authentication);
+ if (https->http_proxy_authentication)
+ free(https->http_proxy_authentication);
+ free(https);
+ fs->fs_outputfile = 0;
+ return 0;
+}
+
+static int
+nullclose(struct fetch_state *fs)
+{
+ return 0;
+}
+
+/*
+ * Process a redirection. This has a small memory leak.
+ */
+static int
+http_redirect(struct fetch_state *fs, char *new, int permanent)
+{
+ struct http_state *https = fs->fs_proto;
+ int num_redirects = https->http_redirected + 1;
+ char *out = safe_strdup(fs->fs_outputfile);
+ int rv;
+
+ if (num_redirects > 5) {
+ warnx("%s: HTTP redirection limit exceeded");
+ return EX_PROTOCOL;
+ }
+
+ free(https->http_hostname);
+ free(https->http_remote_request);
+ free(https->http_decoded_file);
+ free(https);
+ warnx("%s: resource has moved %s to `%s'", out,
+ permanent ? "permanently" : "temporarily", new);
+ rv = http_parse(fs, new);
+ if (rv != 0) {
+ fs->fs_close = nullclose; /* XXX rethink interface? */
+ return rv;
+ }
+ https = fs->fs_proto;
+ https->http_redirected = num_redirects;
+ /*
+ * This ensures that the output file name doesn't suddenly change
+ * under the user's feet. Unfortunately, this results in a small
+ * memory leak. I wish C had garbage collection...
+ */
+ fs->fs_outputfile = out;
+ rv = http_retrieve(fs);
+ return rv;
+}
+
+/*
+ * Read HTML-formatted data from remote and display it on stderr.
+ * This is extremely incomplete, as all it does is delete anything
+ * between angle brackets. However, this is usually good enough for
+ * error messages.
+ */
+static void
+html_display(FILE *remote)
+{
+ char *line;
+ int linelen;
+ int inbracket = 0;
+
+
+ while ((line = fgetln(remote, &linelen)) != 0) {
+ char *end = line + linelen;
+ char *p;
+ int content = 0;
+
+ for (p = line; p < end; p++) {
+ if (*p == '<' && !inbracket) {
+ fwrite(line, 1, (p - line),
+ stderr);
+ inbracket = 1;
+ }
+ if (!inbracket && !content &&
+ *p != '\n' && *p != '\r')
+ content = 1;
+ if (*p == '>' && inbracket) {
+ line = p + 1;
+ inbracket = 0;
+ }
+ }
+ if (content && line < end)
+ fwrite(line, 1, (end - line), stderr);
+ }
+}
+
+/*
+ * Get a file using HTTP. We will try to implement HTTP/1.1 eventually.
+ * This subroutine makes heavy use of the 4.4-Lite standard I/O library,
+ * in particular the `fgetln' which allows us to slurp an entire `line'
+ * (an arbitrary string of non-NUL characters ending in a newline) directly
+ * out of the stdio buffer. This makes interpreting the HTTP headers much
+ * easier, since they are all guaranteed to end in `\r\n' and we can just
+ * ignore the `\r'.
+ */
+static int
+http_retrieve(struct fetch_state *fs)
+{
+ struct http_state *https;
+ FILE *remote, *local;
+ int s;
+ struct sockaddr_in sin;
+ struct msghdr msg;
+#define NIOV 16 /* max is currently 14 */
+ struct iovec iov[NIOV];
+ int n, status;
+ const char *env;
+ int timo;
+ char *line, *new_location;
+ char *errstr = 0;
+ size_t linelen, readresult, writeresult;
+ off_t total_length, restart_from;
+ time_t last_modified, when_to_retry;
+ char *base64ofmd5;
+ static char buf[BUFFER_SIZE];
+ int to_stdout, restarting, redirection, retrying, autherror;
+ char rangebuf[sizeof("Range: bytes=18446744073709551616-\r\n")];
+
+ setup_http_auth();
+
+ https = fs->fs_proto;
+ to_stdout = (strcmp(fs->fs_outputfile, "-") == 0);
+ restarting = fs->fs_restart;
+ redirection = 0;
+ retrying = 0;
+ autherror = 0;
+
+ /*
+ * Figure out the timeout. Prefer the -T command-line value,
+ * otherwise the HTTP_TIMEOUT envar, or else don't time out at all.
+ */
+ if (fs->fs_timeout) {
+ timo = fs->fs_timeout;
+ } else if ((env = getenv("HTTP_TIMEOUT")) != 0) {
+ char *ep;
+ unsigned long ul;
+
+ errno = 0;
+ ul = strtoul(env, &ep, 0);
+ if (*ep != '\0' || *env == '\0' || errno != 0
+ || ul > INT_MAX) {
+ warnx("`%s': invalid timeout", env);
+ return EX_USAGE;
+ }
+ timo = ul;
+ } else {
+ timo = 0;
+ }
+
+ memset(&sin, 0, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof sin;
+ sin.sin_port = htons(https->http_port);
+
+ fs->fs_status = "looking up hostname";
+ if (inet_aton(https->http_hostname, &sin.sin_addr) == 0) {
+ struct hostent *hp;
+
+ /* XXX - do timeouts for name resolution? */
+ hp = gethostbyname2(https->http_hostname, AF_INET);
+ if (hp == 0) {
+ warnx("`%s': cannot resolve: %s", https->http_hostname,
+ hstrerror(h_errno));
+ return EX_NOHOST;
+ }
+ memcpy(&sin.sin_addr, hp->h_addr_list[0], sizeof sin.sin_addr);
+ }
+
+ fs->fs_status = "creating request message";
+ msg.msg_name = (caddr_t)&sin;
+ msg.msg_namelen = sizeof sin;
+ msg.msg_iov = iov;
+ n = 0;
+ msg.msg_control = 0;
+ msg.msg_controllen = 0;
+ msg.msg_flags = MSG_EOF;
+
+#define addstr(Iov, N, Str) \
+ do { \
+ Iov[N].iov_base = (void *)Str; \
+ Iov[N].iov_len = strlen(Iov[n].iov_base); \
+ N++; \
+ } while(0)
+
+retry:
+ addstr(iov, n, "GET ");
+ addstr(iov, n, https->http_remote_request);
+ addstr(iov, n, " HTTP/1.1\r\n");
+ /*
+ * The choice of HTTP/1.1 may be a bit controversial. The
+ * specification says that implementations which are not at
+ * least conditionally compliant MUST NOT call themselves
+ * HTTP/1.1. We choose not to comply with that requirement.
+ * (Eventually we will support the full HTTP/1.1, at which
+ * time this comment will not apply. But it's amusing how
+ * specifications attempt to define behavior for implementations
+ * which aren't obeying the spec in the first place...)
+ */
+ addstr(iov, n, format_http_user_agent());
+ /* do content negotiation here */
+ addstr(iov, n, "Accept: */*\r\n");
+ addstr(iov, n, https->http_host_header);
+ addstr(iov, n, "Connection: close\r\n");
+ if (https->http_proxy_authentication)
+ addstr(iov, n, https->http_proxy_authentication);
+ if (https->http_authentication)
+ addstr(iov, n, https->http_authentication);
+ if (fs->fs_mirror) {
+ struct stat stab;
+
+ errno = 0;
+ if (((!to_stdout && stat(fs->fs_outputfile, &stab) == 0)
+ || (to_stdout && fstat(STDOUT_FILENO, &stab) == 0))
+ && S_ISREG(stab.st_mode)) {
+ addstr(iov, n, "If-Modified-Since: ");
+ addstr(iov, n, format_http_date(stab.st_mtime));
+ addstr(iov, n, "\r\n");
+ } else if (errno != 0) {
+ warn("%s: cannot mirror; will retrieve anew",
+ fs->fs_outputfile);
+ }
+ }
+ if (restarting) {
+ struct stat stab;
+
+ errno = 0;
+ if (((!to_stdout && stat(fs->fs_outputfile, &stab) == 0)
+ || (to_stdout && fstat(STDOUT_FILENO, &stab) == 0))
+ && S_ISREG(stab.st_mode)) {
+ addstr(iov, n, "If-Range: ");
+ addstr(iov, n, format_http_date(stab.st_mtime));
+ addstr(iov, n, "\r\n");
+ sprintf(rangebuf, "Range: bytes=%qd-\r\n",
+ (quad_t)stab.st_size);
+ addstr(iov, n, rangebuf);
+ } else if (errno != 0) {
+ warn("%s: cannot restart; will retrieve anew",
+ fs->fs_outputfile);
+ restarting = 0;
+ } else {
+ warnx("%s: cannot restart; will retrieve anew",
+ fs->fs_outputfile);
+ restarting = 0;
+ }
+ }
+ addstr(iov, n, "\r\n");
+ msg.msg_iovlen = n;
+
+ if (n >= NIOV)
+ err(EX_SOFTWARE, "request vector length exceeded: %d", n);
+
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ warn("socket");
+ return EX_OSERR;
+ }
+
+ remote = fdopen(s, "r");
+ if (remote == 0) {
+ warn("fdopen");
+ close(s);
+ return EX_OSERR;
+ }
+
+ fs->fs_status = "sending request message";
+ setup_sigalrm();
+ alarm(timo);
+ if (sendmsg(s, &msg, MSG_EOF) < 0) {
+ warn("sendmsg: %s", https->http_hostname);
+ fclose(remote);
+ return EX_OSERR;
+ }
+
+got100reply:
+ fs->fs_status = "reading reply status";
+ alarm(timo);
+ line = fgetln(remote, &linelen);
+ alarm(0);
+ if (line == 0) {
+ if (ferror(remote)) {
+ warn("reading reply from %s", https->http_hostname);
+ fclose(remote);
+ unsetup_sigalrm();
+ return EX_OSERR;
+ } else {
+ warnx("empty reply from %s", https->http_hostname);
+ fclose(remote);
+ unsetup_sigalrm();
+ return EX_PROTOCOL;
+ }
+ }
+ /*
+ * If the other end is HTTP 0.9, then we just suck their
+ * response over; can't do anything fancy. We assume that
+ * the file is a text file, so it is safe to use fgetln()
+ * to suck the entire file. (It had better be, since
+ * we used it to grab the first line.)
+ */
+ if (linelen < 5 || strncasecmp(line, "http", 4) != 0) {
+ if (to_stdout)
+ local = fopen("/dev/stdout", "w");
+ else
+ local = fopen(fs->fs_outputfile, "w");
+ if (local == 0) {
+ warn("%s: fopen", fs->fs_outputfile);
+ fclose(remote);
+ unsetup_sigalrm();
+ return EX_OSERR;
+ }
+ fs->fs_status = "retrieving from HTTP/0.9 server";
+ display(fs, -1, 0);
+
+ do {
+ writeresult = fwrite(line, 1, linelen, local);
+ display(fs, -1, writeresult);
+ if (writeresult != linelen)
+ break;
+ alarm(timo);
+ line = fgetln(remote, &linelen);
+ alarm(0);
+ } while(line != 0);
+ unsetup_sigalrm();
+
+ if (ferror(local)) {
+ warn("%s", fs->fs_outputfile);
+ fclose(local);
+ fclose(remote);
+ rm(fs);
+ return EX_OSERR;
+ } else if(ferror(remote)) {
+ warn("%s", https->http_hostname);
+ fclose(local);
+ fclose(remote);
+ rm(fs);
+ return EX_OSERR;
+ }
+ fclose(local);
+ fclose(remote);
+ display(fs, -1, -1);
+ return 0;
+ }
+ /*
+ * OK. The other end is doing HTTP 1.0 at the very least.
+ * This means that some of the fancy stuff is at least possible.
+ */
+ line[linelen - 1] = '\0'; /* turn line into a string */
+ status = http_first_line(line);
+
+ /* In the future, we might handle redirection and other responses. */
+ switch(status) {
+ case 100: /* Continue */
+ goto got100reply;
+ case 200: /* Here come results */
+ case 203: /* Non-Authoritative Information */
+ restarting = 0;
+ break;
+ case 206: /* Here come partial results */
+ /* can only happen when restarting */
+ break;
+ case 301: /* Resource has moved permanently */
+ if (!fs->fs_auto_retry)
+ errstr = safe_strdup(line);
+ else
+ redirection = 301;
+ break;
+ case 302: /* Resource has moved temporarily */
+ /*
+ * We don't test fs->fs_auto_retry here so that this
+ * sort of redirection is transparent to the user.
+ */
+ redirection = 302;
+ break;
+ case 304: /* Object is unmodified */
+ if (fs->fs_mirror) {
+ fclose(remote);
+ unsetup_sigalrm();
+ return 0;
+ }
+ errstr = safe_strdup(line);
+ break;
+ case 401: /* Unauthorized */
+ if (https->http_authentication)
+ errstr = safe_strdup(line);
+ else
+ autherror = 401;
+ break;
+ case 407: /* Proxy Authentication Required */
+ if (https->http_proxy_authentication)
+ errstr = safe_strdup(line);
+ else
+ autherror = 407;
+ break;
+ case 503: /* Service Unavailable */
+ if (!fs->fs_auto_retry)
+ errstr = safe_strdup(line);
+ else
+ retrying = 503;
+ break;
+
+ default:
+ errstr = safe_strdup(line);
+ break;
+ }
+
+ total_length = -1; /* -1 means ``don't know'' */
+ last_modified = when_to_retry = -1;
+ base64ofmd5 = 0;
+ new_location = 0;
+ restart_from = 0;
+ fs->fs_status = "parsing reply headers";
+
+ while((line = fgetln(remote, &linelen)) != 0) {
+ char *value, *ep;
+ enum http_header header;
+ unsigned long ul;
+
+ line[linelen - 1] = '\0';
+ header = http_parse_header(line, &value);
+
+ if (header == ht_end_of_header)
+ break;
+
+ switch(header) {
+ case ht_content_length:
+ errno = 0;
+ ul = strtoul(value, &ep, 10);
+ if (errno != 0 || *ep)
+ warnx("invalid Content-Length: `%s'", value);
+ if (!restarting)
+ total_length = ul;
+ break;
+
+ case ht_last_modified:
+ last_modified = parse_http_date(value);
+ if (last_modified == -1 && fs->fs_verbose > 0)
+ warnx("invalid Last-Modified: `%s'", value);
+ break;
+
+ case ht_content_md5:
+ base64ofmd5 = safe_strdup(value);
+ break;
+
+ case ht_content_range:
+ if (!restarting) /* XXX protocol error */
+ break;
+
+ /* NB: we might have to restart from farther back
+ than we asked. */
+ status = parse_http_content_range(value, &restart_from,
+ &total_length);
+ /* If we couldn't understand the reply, get the whole
+ thing. */
+ if (status) {
+ restarting = 0;
+doretry:
+ fclose(remote);
+ if (base64ofmd5)
+ free(base64ofmd5);
+ if (new_location)
+ free(new_location);
+ restart_from = 0;
+ n = 0;
+ goto retry;
+ }
+ break;
+
+ case ht_location:
+ if (redirection) {
+ char *s = value;
+ while (*s && !isspace(*s))
+ s++;
+ new_location = safe_strndup(value, s - value);
+ }
+ break;
+
+ case ht_transfer_encoding:
+ warnx("%s: %s specified a Transfer-Encoding: %s",
+ fs->fs_outputfile, https->http_hostname,
+ value);
+ warnx("%s: output file may be uninterpretable",
+ fs->fs_outputfile);
+ break;
+
+ case ht_retry_after:
+ if (!retrying)
+ break;
+
+ errno = 0;
+ ul = strtoul(value, &ep, 10);
+ if (errno != 0 || (*ep && !isspace(*ep))) {
+ time_t when;
+ when = parse_http_date(value);
+ if (when == -1)
+ break;
+ when_to_retry = when;
+ } else {
+ when_to_retry = time(0) + ul;
+ }
+ break;
+
+ case ht_www_authenticate:
+ if (autherror != 401)
+ break;
+
+ status = process_http_auth(fs, value, autherror);
+ if (status != 0)
+ goto cantauth;
+ break;
+
+ case ht_proxy_authenticate:
+ if (autherror != 407)
+ break;
+ status = process_http_auth(fs, value, autherror);
+ if (status != 0)
+ goto cantauth;
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (autherror == 401 && https->http_authentication)
+ goto doretry;
+ if (autherror == 407 && https->http_proxy_authentication)
+ goto doretry;
+ if (autherror) {
+ goto spewerror;
+ }
+
+ if (retrying) {
+ int howlong;
+
+ if (when_to_retry == -1) {
+ errstr = safe_strdup("HTTP/1.1 503 Service Unavailable");
+ goto spewerror;
+ }
+ howlong = when_to_retry - time(0);
+ if (howlong < 30)
+ howlong = 30;
+
+ warnx("%s: service unavailable; retrying in %d seconds",
+ https->http_hostname, howlong);
+ fs->fs_status = "waiting to retry";
+ sleep(howlong);
+ goto doretry;
+ }
+
+ if (errstr != 0) {
+spewerror:
+ warnx("%s: %s: HTTP server returned error code %d",
+ fs->fs_outputfile, https->http_hostname, status);
+ if (fs->fs_verbose > 1) {
+ fputs(errstr, stderr);
+ fputc('\n', stderr);
+ html_display(remote);
+ }
+ free(errstr);
+ fclose(remote);
+ unsetup_sigalrm();
+ return EX_UNAVAILABLE;
+ }
+
+ if (redirection && new_location) {
+ fclose(remote);
+ if (base64ofmd5)
+ free(base64ofmd5);
+ fs->fs_status = "processing redirection";
+ status = http_redirect(fs, new_location, redirection == 301);
+ free(new_location);
+ return status;
+ } else if (redirection) {
+ warnx("%s: redirection but no new location",
+ fs->fs_outputfile);
+ fclose(remote);
+ if (base64ofmd5)
+ free(base64ofmd5);
+ return EX_PROTOCOL;
+ }
+
+ fs->fs_status = "retrieving file from HTTP/1.x server";
+
+ /*
+ * OK, if we got here, then we have finished parsing the header
+ * and have read the `\r\n' line which denotes the end of same.
+ * We may or may not have a good idea of the length of the file
+ * or its modtime. At this point we will have to deal with
+ * any special byte-range, content-negotiation, redirection,
+ * or authentication, and probably jump back up to the top,
+ * once we implement those features. So, all we have left to
+ * do is open up the output file and copy data from input to
+ * output until EOF.
+ */
+ if (to_stdout)
+ local = fopen("/dev/stdout", restarting ? "a" : "w");
+ else
+ local = fopen(fs->fs_outputfile, restarting ? "a" : "w");
+ if (local == 0) {
+ warn("%s: fopen", fs->fs_outputfile);
+ fclose(remote);
+ unsetup_sigalrm();
+ return EX_OSERR;
+ }
+
+ fs->fs_modtime = last_modified;
+ fseek(local, restart_from, SEEK_SET); /* XXX truncation off_t->long */
+ display(fs, total_length, restart_from); /* XXX truncation */
+
+ /*
+ * Eventually this loop will be separated out as http_suck(), and
+ * there will be a separate http_suck_chunked() to deal with that
+ * Transfer-Encoding.
+ */
+ do {
+ alarm(timo);
+ readresult = fread(buf, 1, sizeof buf, remote);
+ alarm(0);
+
+ if (readresult == 0)
+ break;
+ display(fs, total_length, readresult);
+
+ writeresult = fwrite(buf, 1, readresult, local);
+ } while (writeresult == readresult);
+
+ status = errno; /* save errno for warn(), below, if needed */
+ display(fs, total_length, -1); /* do here in case we have to warn */
+ errno = status;
+
+ if (ferror(remote)) {
+ warn("reading remote file from %s", https->http_hostname);
+ status = EX_OSERR;
+ } else if(ferror(local)) {
+ warn("`%s': fwrite", fs->fs_outputfile);
+ status = EX_OSERR;
+ } else {
+ status = 0;
+ }
+ if (base64ofmd5) {
+ /*
+ * Ack. When restarting, the MD5 only covers the parts
+ * we are getting, not the whole thing.
+ */
+ fseek(local, restart_from, SEEK_SET);
+ fs->fs_status = "computing MD5 message digest";
+ status = check_md5(local, base64ofmd5);
+ free(base64ofmd5);
+ }
+
+ fclose(local);
+out:
+ unsetup_sigalrm();
+ fclose(remote);
+
+ if (status != 0)
+ rm(fs);
+ else
+ adjmodtime(fs);
+
+ return status;
+#undef addstr
+
+cantauth:
+ warnx("%s: cannot authenticate with %s %s",
+ fs->fs_outputfile,
+ (autherror == 401) ? "server" : "proxy",
+ https->http_hostname);
+ status = EX_NOPERM;
+ goto out;
+}
+
+/*
+ * The format of the response line for an HTTP request is:
+ * HTTP/V.vv{WS}999{WS}Explanatory text for humans to read\r\n
+ * Old pre-HTTP/1.0 servers can return
+ * HTTP{WS}999{WS}Explanatory text for humans to read\r\n
+ * Where {WS} represents whitespace (spaces and/or tabs) and 999
+ * is a machine-interprable result code. We return the integer value
+ * of that result code, or the impossible value `0' if we are unable to
+ * parse the result.
+ */
+static int
+http_first_line(const char *line)
+{
+ char *ep;
+ unsigned long ul;
+
+ if (strncasecmp(line, "http", 4) != 0)
+ return 0;
+
+ line += 4;
+ while (*line && !isspace(*line)) /* skip non-whitespace */
+ line++;
+ while (*line && isspace(*line)) /* skip first whitespace */
+ line++;
+
+ errno = 0;
+ ul = strtoul(line, &ep, 10);
+ if (errno != 0 || ul > 999 || ul < 100 || !isspace(*ep))
+ return 0;
+ return ul;
+}
+
+/*
+ * The format of a header line for an HTTP request is:
+ * Header-Name: header-value (with comments in parens)\r\n
+ * This would be a nice application for gperf(1), except that the
+ * names are case-insensitive and gperf can't handle that.
+ */
+static enum http_header
+http_parse_header(char *line, char **valuep)
+{
+ char *colon, *value;
+
+ if (*line == '\0' /* protocol error! */
+ || (line[0] == '\r' && line[1] == '\0'))
+ return ht_end_of_header;
+
+ colon = strchr(line, ':');
+ if (colon == 0)
+ return ht_syntax_error;
+ *colon = '\0';
+
+ for (value = colon + 1; *value && isspace(*value); value++)
+ ; /* do nothing */
+
+ /* Trim trailing whitespace (including \r). */
+ *valuep = value;
+ value += strlen(value) - 1;
+ while (value > *valuep && isspace(*value))
+ value--;
+ *++value = '\0';
+
+#define cmp(name, num) do { if (!strcasecmp(line, name)) return num; } while(0)
+ cmp("Accept-Ranges", ht_accept_ranges);
+ cmp("Age", ht_age);
+ cmp("Allow", ht_allow);
+ cmp("Cache-Control", ht_cache_control);
+ cmp("Connection", ht_connection);
+ cmp("Content-Base", ht_content_base);
+ cmp("Content-Encoding", ht_content_encoding);
+ cmp("Content-Language", ht_content_language);
+ cmp("Content-Length", ht_content_length);
+ cmp("Content-Location", ht_content_location);
+ cmp("Content-MD5", ht_content_md5);
+ cmp("Content-Range", ht_content_range);
+ cmp("Content-Type", ht_content_type);
+ cmp("Date", ht_date);
+ cmp("ETag", ht_etag);
+ cmp("Expires", ht_expires);
+ cmp("Last-Modified", ht_last_modified);
+ cmp("Location", ht_location);
+ cmp("Pragma", ht_pragma);
+ cmp("Proxy-Authenticate", ht_proxy_authenticate);
+ cmp("Public", ht_public);
+ cmp("Retry-After", ht_retry_after);
+ cmp("Server", ht_server);
+ cmp("Transfer-Encoding", ht_transfer_encoding);
+ cmp("Upgrade", ht_upgrade);
+ cmp("Vary", ht_vary);
+ cmp("Via", ht_via);
+ cmp("WWW-Authenticate", ht_www_authenticate);
+ cmp("Warning", ht_warning);
+#undef cmp
+ return ht_unknown;
+}
+
+/*
+ * Compute the RSA Data Security, Inc., MD5 Message Digest of the file
+ * given in `fp', see if it matches the one given in base64 encoding by
+ * `base64ofmd5'. Warn and return an error if it doesn't.
+ */
+static int
+check_md5(FILE *fp, char *base64ofmd5) {
+ MD5_CTX ctx;
+ unsigned char digest[16];
+ char buf[512];
+ size_t len;
+ char *ourval;
+
+ MD5Init(&ctx);
+ while ((len = fread(buf, 1, sizeof buf, fp)) != 0) {
+ MD5Update(&ctx, buf, len);
+ }
+ MD5Final(digest, &ctx);
+ ourval = to_base64(digest, 16);
+ if (strcmp(ourval, base64ofmd5) != 0) {
+ warnx("MD5 digest mismatch: %s, should be %s", ourval,
+ base64ofmd5);
+ free(ourval);
+ return EX_DATAERR;
+ }
+ free(ourval);
+ return 0;
+}
+
+static const char *wkdays[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+static const char *months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
+ "Nov", "Dec"
+};
+
+/*
+ * Interpret one of the three possible formats for an HTTP date.
+ * All of them are really bogus; HTTP should use either ISO 8601
+ * or NTP timestamps. We make some attempt to accept a subset of 8601
+ * format. The three standard formats are all fixed-length subsets of their
+ * respective standards (except 8601, which puts all of the stuff we
+ * care about up front).
+ */
+static time_t
+parse_http_date(char *string)
+{
+ static struct tm tm; /* get good initialization */
+ time_t rv;
+ const char *tz;
+ int i;
+
+ /* 8601 has the shortest minimum length */
+ if (strlen(string) < 15)
+ return -1;
+
+ if (isdigit(*string)) {
+ /* ISO 8601: 19970127T134551stuffwedon'tcareabout */
+ for (i = 0; i < 15; i++) {
+ if (i != 8 && !isdigit(string[i]))
+ break;
+ }
+ if (i < 15)
+ return -1;
+#define digit(x) (string[x] - '0')
+ tm.tm_year = (digit(0) * 1000
+ + digit(1) * 100
+ + digit(2) * 10
+ + digit(3)) - 1900;
+ tm.tm_mon = digit(4) * 10 + digit(5) - 1;
+ tm.tm_mday = digit(6) * 10 + digit(7);
+ if (string[8] != 'T' && string[8] != 't' && string[8] != ' ')
+ return -1;
+ tm.tm_hour = digit(9) * 10 + digit(10);
+ tm.tm_min = digit(11) * 10 + digit(12);
+ tm.tm_sec = digit(13) * 10 + digit(14);
+ /* We don't care about the rest of the stuff after the secs. */
+ } else if (string[3] == ',') {
+ /* Mon, 27 Jan 1997 14:24:35 stuffwedon'tcareabout */
+ if (strlen(string) < 25)
+ return -1;
+ string += 5; /* skip over day-of-week */
+ if (!(isdigit(string[0]) && isdigit(string[1])))
+ return -1;
+ tm.tm_mday = digit(0) * 10 + digit(1);
+ for (i = 0; i < 12; i++) {
+ if (strncasecmp(months[i], &string[3], 3) == 0)
+ break;
+ }
+ if (i >= 12)
+ return -1;
+ tm.tm_mon = i;
+
+ if (sscanf(&string[7], "%d %d:%d:%d", &i, &tm.tm_hour,
+ &tm.tm_min, &tm.tm_sec) != 4)
+ return -1;
+ tm.tm_year = i - 1900;
+
+ } else if (string[3] == ' ') {
+ /* Mon Jan 27 14:25:20 1997 */
+ if (strlen(string) < 25)
+ return -1;
+ string += 4;
+ for (i = 0; i < 12; i++) {
+ if (strncasecmp(string, months[i], 3) == 0)
+ break;
+ }
+ if (i >= 12)
+ return -1;
+ tm.tm_mon = i;
+ if (sscanf(&string[4], "%d %d:%d:%d %u", &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &i)
+ != 5)
+ return -1;
+ tm.tm_year = i - 1900;
+ } else {
+ /* Monday, 27-Jan-97 14:31:09 stuffwedon'tcareabout */
+ char *comma = strchr(string, ',');
+ char mname[4];
+
+ if (comma == 0)
+ return -1;
+ string = comma + 1;
+ if (strlen(string) < 19)
+ return -1;
+ string++;
+ mname[4] = '\0';
+ if (sscanf(string, "%d-%c%c%c-%d %d:%d:%d", &tm.tm_mday,
+ mname, mname + 1, mname + 2, &tm.tm_year,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 8)
+ return -1;
+ for (i = 0; i < 12; i++) {
+ if (strcasecmp(months[i], mname))
+ break;
+ }
+ if (i >= 12)
+ return -1;
+ tm.tm_mon = i;
+ }
+#undef digit
+
+ if (tm.tm_sec > 60 || tm.tm_min > 59 || tm.tm_hour > 23
+ || tm.tm_mday > 31 || tm.tm_mon > 11)
+ return -1;
+ if (tm.tm_sec < 0 || tm.tm_min < 0 || tm.tm_hour < 0
+ || tm.tm_mday < 0 || tm.tm_mon < 0 || tm.tm_year < 0)
+ return -1;
+
+ tz = getenv("TZ");
+ setenv("TZ", "UTC0", 1);
+ tzset();
+ rv = mktime(&tm);
+ if (tz)
+ setenv("TZ", tz, 1);
+ else
+ unsetenv("TZ");
+ return rv;
+}
+
+static char *
+format_http_date(time_t when)
+{
+ struct tm *tm;
+ static char buf[30];
+
+ tm = gmtime(&when);
+ if (tm == 0)
+ return 0;
+#ifndef HTTP_DATE_ISO_8601
+ sprintf(buf, "%s, %02d %s %04d %02d:%02d:%02d GMT",
+ wkdays[tm->tm_wday], tm->tm_mday, months[tm->tm_mon],
+ tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
+#else /* ISO 8601 */
+ sprintf(buf, "%04d%02d%02dT%02d%02d%02d+0000",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+#endif
+ return buf;
+}
+
+static char *
+format_http_user_agent(void)
+{
+ static char buf[128];
+ static int inited;
+
+ if (!inited) {
+ int mib[2];
+ char ostype[128], osrelease[128], machine[128];
+ size_t len;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_OSTYPE;
+ len = sizeof ostype;
+ if (sysctl(mib, 2, ostype, &len, 0, 0) < 0) {
+ warn("sysctl");
+ ostype[0] = '\0';
+ }
+ mib[1] = KERN_OSRELEASE;
+ len = sizeof osrelease;
+ if (sysctl(mib, 2, osrelease, &len, 0, 0) < 0) {
+ warn("sysctl");
+ osrelease[0] = '\0';
+ }
+ mib[0] = CTL_HW;
+ mib[1] = HW_MACHINE;
+ len = sizeof machine;
+ if (sysctl(mib, 2, machine, &len, 0, 0) < 0) {
+ warn("sysctl");
+ machine[0] = '\0';
+ }
+
+ snprintf(buf, sizeof buf,
+ "User-Agent: " FETCH_VERSION " %s/%s (%s)\r\n",
+ ostype, osrelease, machine);
+ }
+ return buf;
+}
+
+/*
+ * Parse a Content-Range return header from the server. RFC 2066 defines
+ * this header to have the format:
+ * Content-Range: bytes 12345-67890/123456
+ * Since we always ask for the whole rest of the file, we consider it an
+ * error if the reply doesn't claim to give it to us.
+ */
+static int
+parse_http_content_range(char *orig, off_t *restart_from, off_t *total_length)
+{
+ u_quad_t first, last, total;
+ char *ep;
+
+ if (strncasecmp(orig, "bytes", 5) != 0) {
+ warnx("unknown Content-Range unit: `%s'", orig);
+ return EX_PROTOCOL;
+ }
+
+ orig += 5;
+ while (*orig && isspace(*orig))
+ orig++;
+
+ errno = 0;
+ first = strtouq(orig, &ep, 10);
+ if (errno != 0 || *ep != '-') {
+ warnx("invalid Content-Range: `%s'", orig);
+ return EX_PROTOCOL;
+ }
+ last = strtouq(ep + 1, &ep, 10);
+ if (errno != 0 || *ep != '/' || last < first) {
+ warnx("invalid Content-Range: `%s'", orig);
+ return EX_PROTOCOL;
+ }
+ total = strtouq(ep + 1, &ep, 10);
+ if (errno != 0 || !(*ep == '\0' || isspace(*ep))) {
+ warnx("invalid Content-Range: `%s'", orig);
+ return EX_PROTOCOL;
+ }
+
+ if (last + 1 != total) {
+ warnx("HTTP server did not return requested Content-Range");
+ return EX_PROTOCOL;
+ }
+
+ *restart_from = first;
+ *total_length = last;
+ return 0;
+}
+
+/*
+ * Do HTTP authentication. We only do ``basic'' right now, but
+ * MD5 ought to be fairly easy. The hard part is actually teasing
+ * apart the header, which is fairly badly designed (so what else is
+ * new?).
+ */
+static char *
+getauthparam(char *params, const char *name)
+{
+ char *rv;
+ enum state { normal, quoted } state;
+ while (*params) {
+ if (strncasecmp(params, name, strlen(name)) == 0
+ && params[strlen(name)] == '=')
+ break;
+ state = normal;
+ while (*params) {
+ if (state == normal && *params == ',')
+ break;
+ if (*params == '\"')
+ state = (state == quoted) ? normal : quoted;
+ if (*params == '\\' && params[1] != '\0')
+ params++;
+ params++;
+ }
+ }
+
+ if (*params == '\0')
+ return 0;
+ params += strlen(name) + 1;
+ rv = params;
+ state = normal;
+ while (*params) {
+ if (state == normal && *params == ',')
+ break;
+ if (*params == '\"')
+ state = (state == quoted) ? normal : quoted;
+ if (*params == '\\' && params[1] != '\0')
+ params++;
+ params++;
+ }
+ if (params[-1] == '\"')
+ params[-1] = '\0';
+ else
+ params[0] = '\0';
+
+ if (*rv == '\"')
+ rv++;
+ return rv;
+}
+
+static int
+process_http_auth(struct fetch_state *fs, char *hdr, int autherr)
+{
+ enum state { normal, quoted } state;
+ char *scheme, *params, *nscheme, *realm;
+ struct http_auth *ha;
+
+ do {
+ scheme = params = hdr;
+ /* Look for end of scheme name. */
+ while (*params && !isspace(*params))
+ params++;
+
+ if (*params == '\0')
+ return EX_PROTOCOL;
+
+ /* Null-terminate scheme and skip whitespace. */
+ while (*params && isspace(*params))
+ *params++ = '\0';
+
+ /* Semi-parse parameters to find their end. */
+ nscheme = params;
+ state = normal;
+ while (*nscheme) {
+ if (state == normal && isspace(*nscheme))
+ break;
+ if (*nscheme == '\"')
+ state = (state == quoted) ? normal : quoted;
+ if (*nscheme == '\\' && nscheme[1] != '\0')
+ nscheme++;
+ nscheme++;
+ }
+
+ /* Null-terminate parameters and skip whitespace. */
+ while (*nscheme && isspace(*nscheme))
+ *nscheme++ = '\0';
+
+ realm = getauthparam(params, "realm");
+ if (realm == 0) {
+ scheme = nscheme;
+ continue;
+ }
+
+ if (autherr == 401)
+ ha = find_http_auth(&http_auth, scheme, realm);
+ else
+ ha = find_http_auth(&http_proxy_auth, scheme, realm);
+
+ if (ha)
+ return ha->ha_ham->ham_doauth(fs, ha, autherr == 407);
+ } while (*scheme);
+ return EX_NOPERM;
+}
+
+static void
+parse_http_auth_env(const char *env, struct http_auth_head *ha_tqh)
+{
+ char *nenv, *p, *scheme, *realm, *params;
+ struct http_auth *ha;
+ struct http_auth_method *ham;
+
+ nenv = alloca(strlen(env) + 1);
+ strcpy(nenv, env);
+
+ while ((p = strsep(&nenv, " \t")) != 0) {
+ scheme = strsep(&p, ":");
+ if (scheme == 0 || *scheme == '\0')
+ continue;
+ realm = strsep(&p, ":");
+ if (realm == 0 || *realm == '\0')
+ continue;
+ params = (p && *p) ? p : 0;
+ for (ham = http_auth_methods; ham->ham_scheme; ham++) {
+ if (strcasecmp(scheme, ham->ham_scheme) == 0)
+ break;
+ }
+ if (ham == 0)
+ continue;
+ ha = safe_malloc(sizeof *ha);
+ ha->ha_scheme = safe_strdup(scheme);
+ ha->ha_realm = safe_strdup(realm);
+ ha->ha_params = params ? safe_strdup(params) : 0;
+ ha->ha_ham = ham;
+ TAILQ_INSERT_TAIL(ha_tqh, ha, ha_link);
+ }
+}
+
+/*
+ * Look up an authentication method. Automatically clone wildcards
+ * into fully-specified entries.
+ */
+static struct http_auth *
+find_http_auth(struct http_auth_head *tqh, const char *scm, const char *realm)
+{
+ struct http_auth *ha;
+
+ for (ha = tqh->tqh_first; ha; ha = ha->ha_link.tqe_next) {
+ if (strcasecmp(ha->ha_scheme, scm) == 0
+ && strcasecmp(ha->ha_realm, realm) == 0)
+ return ha;
+ }
+
+ for (ha = tqh->tqh_first; ha; ha = ha->ha_link.tqe_next) {
+ if (strcasecmp(ha->ha_scheme, scm) == 0
+ && strcmp(ha->ha_realm, "*") == 0)
+ break;
+ }
+ if (ha != 0) {
+ struct http_auth *ha2;
+
+ ha2 = safe_malloc(sizeof *ha2);
+ ha2->ha_scheme = safe_strdup(scm);
+ ha2->ha_realm = safe_strdup(realm);
+ ha2->ha_params = ha->ha_params ? safe_strdup(ha->ha_params) :0;
+ ha2->ha_ham = ha->ha_ham;
+ TAILQ_INSERT_TAIL(tqh, ha2, ha_link);
+ ha = ha2;
+ }
+
+ return ha;
+}
+
+static void
+setup_http_auth(void)
+{
+ const char *envar;
+ static int once;
+
+ if (once)
+ return;
+ once = 1;
+
+ TAILQ_INIT(&http_auth);
+ TAILQ_INIT(&http_proxy_auth);
+ envar = getenv("HTTP_AUTH");
+ if (envar)
+ parse_http_auth_env(envar, &http_auth);
+
+ envar = getenv("HTTP_PROXY_AUTH");
+ if (envar)
+ parse_http_auth_env(envar, &http_proxy_auth);
+}
+
+static int
+basic_doauth(struct fetch_state *fs, struct http_auth *ha, int isproxy)
+{
+ struct http_state *https = fs->fs_proto;
+ char *user;
+ char *pass;
+ char *enc;
+ char **hdr;
+ size_t userlen;
+ FILE *fp;
+
+ if (!isatty(0) &&
+ (ha->ha_params == 0 || strchr(ha->ha_params, ':') == 0))
+ return EX_NOPERM;
+
+ fp = fopen("/dev/tty", "r+");
+ if (fp == 0) {
+ warn("opening /dev/tty");
+ return EX_OSERR;
+ }
+ if (ha->ha_params == 0) {
+ fprintf(fp, "Enter `basic' user name for realm `%s': ",
+ ha->ha_realm);
+ fflush(fp);
+ user = fgetln(stdin, &userlen);
+ if (user == 0 || userlen < 1) { /* longer name? */
+ fclose(fp);
+ return EX_NOPERM;
+ }
+ if (user[userlen - 1] == '\n')
+ user[userlen - 1] = '\0';
+ else
+ user[userlen] = '\0';
+ user = safe_strdup(user);
+ pass = 0;
+ } else if ((pass = strchr(ha->ha_params, ':')) == 0) {
+ user = safe_strdup(ha->ha_params);
+ free(ha->ha_params);
+ }
+
+ if (pass == 0) {
+ pass = getpass("Password: ");
+ ha->ha_params = safe_malloc(strlen(user) + 2 + strlen(pass));
+ strcpy(ha->ha_params, user);
+ strcat(ha->ha_params, ":");
+ strcat(ha->ha_params, pass);
+ }
+
+ enc = to_base64(ha->ha_params, strlen(ha->ha_params));
+
+ hdr = isproxy ? &https->http_proxy_authentication
+ : &https->http_authentication;
+ if (*hdr)
+ free(*hdr);
+ *hdr = safe_malloc(sizeof("Proxy-Authorization: basic \r\n")
+ + strlen(enc));
+ if (isproxy)
+ strcpy(*hdr, "Proxy-Authorization");
+ else
+ strcpy(*hdr, "Authorization");
+ strcat(*hdr, ": Basic ");
+ strcat(*hdr, enc);
+ strcat(*hdr, "\r\n");
+ free(enc);
+ return 0;
+}
diff --git a/usr.bin/fetch/main.c b/usr.bin/fetch/main.c
new file mode 100644
index 0000000..999aff6
--- /dev/null
+++ b/usr.bin/fetch/main.c
@@ -0,0 +1,353 @@
+/*-
+ * Copyright (c) 1996
+ * Jean-Marc Zucconi
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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: main.c,v 1.39 1997/07/01 06:37:34 charnier Exp $ */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h> /* needed for INT_MAX */
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <sys/param.h> /* for MAXHOSTNAMELEN */
+#include <sys/time.h> /* for struct timeval, gettimeofday */
+
+#include "fetch.h"
+
+static struct fetch_state clean_fetch_state;
+static sigjmp_buf sigbuf;
+static int get(struct fetch_state *volatile fs);
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: fetch [-DHILMNPRTValmnpqrv] [-o outputfile]",
+ " [-f file -h host [-c dir] | URL]");
+ exit(EX_USAGE);
+}
+
+
+int
+main(int argc, char *const *argv)
+{
+ int c;
+ char *ep;
+ struct fetch_state fs;
+ const char *change_to_dir, *file_to_get, *hostname;
+ int error, rv;
+ unsigned long l;
+
+ init_schemes();
+ fs = clean_fetch_state;
+ fs.fs_verbose = 1;
+ change_to_dir = file_to_get = hostname = 0;
+
+ while ((c = getopt(argc, argv, "ac:D:f:h:HilLmMnNo:pPqRrT:vV:")) != -1) {
+ switch (c) {
+ case 'D': case 'H': case 'I': case 'N': case 'L': case 'V':
+ break; /* ncftp compatibility */
+
+ case 'a':
+ fs.fs_auto_retry = 1;
+ break;
+ case 'c':
+ change_to_dir = optarg;
+ break;
+
+ case 'f':
+ file_to_get = optarg;
+ break;
+
+ case 'h':
+ hostname = optarg;
+ break;
+
+ case 'l':
+ fs.fs_linkfile = 1;
+ break;
+
+ case 'm': case 'M':
+ fs.fs_mirror = 1;
+ break;
+
+ case 'n':
+ fs.fs_newtime = 1;
+ break;
+
+ case 'o':
+ fs.fs_outputfile = optarg;
+ break;
+
+ case 'p': case 'P':
+ fs.fs_passive_mode = 1;
+ break;
+
+ case 'q':
+ fs.fs_verbose = 0;
+ break;
+
+ case 'r':
+ fs.fs_restart = 1;
+ break;
+
+ case 'R':
+ fs.fs_precious = 1;
+ break;
+
+ case 'T':
+ /* strtol sets errno to ERANGE in the case of overflow */
+ errno = 0;
+ l = strtoul(optarg, &ep, 0);
+ if (!optarg[0] || *ep || errno != 0 || l > INT_MAX)
+ errx(EX_USAGE, "invalid timeout value: `%s'",
+ optarg);
+ fs.fs_timeout = l;
+ break;
+
+ case 'v':
+ if (fs.fs_verbose < 2)
+ fs.fs_verbose = 2;
+ else
+ fs.fs_verbose++;
+ break;
+
+ default:
+ case '?':
+ usage();
+ }
+ }
+
+ clean_fetch_state = fs; /* preserve option settings */
+
+ if (argv[optind] && (hostname || change_to_dir || file_to_get)) {
+ warnx("cannot use -h, -c, or -f with a URI argument");
+ usage();
+ }
+
+ if (fs.fs_mirror && fs.fs_restart)
+ errx(EX_USAGE, "-m and -r are mutually exclusive.");
+
+ if (argv[optind] == 0) {
+ char *uri;
+
+ if (hostname == 0) hostname = "localhost";
+ if (change_to_dir == 0) change_to_dir = "";
+ if (file_to_get == 0) {
+ usage();
+ }
+
+ uri = alloca(sizeof("ftp://") + strlen(hostname) +
+ strlen(change_to_dir) + 2 + strlen(file_to_get));
+ strcpy(uri, "ftp://");
+ strcat(uri, hostname);
+ /*
+ * XXX - we should %-map a leading `/' into `%2f', but for
+ * anonymous FTP it is unlikely to matter. Still, it would
+ * be better to follow the spec.
+ */
+ if (change_to_dir[0] != '/')
+ strcat(uri, "/");
+ strcat(uri, change_to_dir);
+ if (file_to_get[0] != '/' && uri[strlen(uri) - 1] != '/')
+ strcat(uri, "/");
+ strcat(uri, file_to_get);
+
+ error = parse_uri(&fs, uri);
+ if (error)
+ return error;
+ return get(&fs);
+ }
+
+ for (rv = 0; argv[optind] != 0; optind++) {
+ error = parse_uri(&fs, argv[optind]);
+ if (error) {
+ rv = error;
+ continue;
+ }
+
+ error = get(&fs);
+ if (error) {
+ rv = error;
+ }
+ fs = clean_fetch_state;
+ }
+ return rv;
+}
+
+/*
+ * The signal handling is probably more complex than it needs to be,
+ * but it doesn't cost a lot, so we'll be extra-careful. Using
+ * siglongjmp() to get out of the signal handler allows us to
+ * call rm() without having to store the state variable in some global
+ * spot where the signal handler can get at it. We also obviate the need
+ * for a separate timeout signal handler.
+ */
+static int
+get(struct fetch_state *volatile fs)
+{
+ volatile int error;
+ struct sigaction oldhup, oldint, oldquit, oldterm;
+ struct sigaction catch;
+ volatile sigset_t omask;
+
+ sigemptyset(&catch.sa_mask);
+ sigaddset(&catch.sa_mask, SIGHUP);
+ sigaddset(&catch.sa_mask, SIGINT);
+ sigaddset(&catch.sa_mask, SIGQUIT);
+ sigaddset(&catch.sa_mask, SIGTERM);
+ sigaddset(&catch.sa_mask, SIGALRM);
+ catch.sa_handler = catchsig;
+ catch.sa_flags = 0;
+
+ sigprocmask(SIG_BLOCK, &catch.sa_mask, (sigset_t *)&omask);
+ sigaction(SIGHUP, &catch, &oldhup);
+ sigaction(SIGINT, &catch, &oldint);
+ sigaction(SIGQUIT, &catch, &oldquit);
+ sigaction(SIGTERM, &catch, &oldterm);
+
+ error = sigsetjmp(sigbuf, 0);
+ if (error == SIGALRM) {
+ rm(fs);
+ unsetup_sigalrm();
+ fprintf(stderr, "\n"); /* just in case */
+ warnx("%s: %s: timed out", fs->fs_outputfile, fs->fs_status);
+ goto close;
+ } else if (error) {
+ rm(fs);
+ fprintf(stderr, "\n"); /* just in case */
+ warnx("%s: interrupted by signal: %s", fs->fs_status,
+ sys_signame[error]);
+ sigdelset(&omask, error);
+ signal(error, SIG_DFL);
+ sigprocmask(SIG_SETMASK, (sigset_t *)&omask, 0);
+ raise(error); /* so that it gets reported as such */
+ }
+
+ sigprocmask(SIG_SETMASK, (sigset_t *)&omask, 0);
+ error = fs->fs_retrieve(fs);
+
+close:
+ sigaction(SIGHUP, &oldhup, 0);
+ sigaction(SIGINT, &oldint, 0);
+ sigaction(SIGQUIT, &oldquit, 0);
+ sigaction(SIGTERM, &oldterm, 0);
+ fs->fs_close(fs);
+
+ return error;
+}
+
+
+/*
+ * Utility functions
+ */
+
+/*
+ * Handle all signals by jumping back into get().
+ */
+void
+catchsig(int sig)
+{
+ siglongjmp(sigbuf, sig);
+}
+
+/* Used to generate the progress display when not in quiet mode. */
+void
+display(struct fetch_state *fs, off_t size, ssize_t n)
+{
+ static off_t bytes;
+ static off_t bytestart;
+ static int pr, stdoutatty, init = 0;
+ static struct timeval t0, t_start;
+ static char *s;
+ struct timezone tz;
+ struct timeval t;
+ float d;
+
+ if (!fs->fs_verbose)
+ return;
+ if (init == 0) {
+ init = 1;
+ gettimeofday(&t0, &tz);
+ t_start = t0;
+ bytes = pr = 0;
+ stdoutatty = isatty(STDOUT_FILENO);
+ if (size > 0)
+ asprintf (&s, "Receiving %s (%qd bytes)%s", fs->fs_outputfile,
+ (quad_t)size,
+ size ? "" : " [appending]");
+ else
+ asprintf (&s, "Receiving %s", fs->fs_outputfile);
+ fprintf (stderr, "%s", s);
+ bytestart = bytes = n;
+ return;
+ }
+ gettimeofday(&t, &tz);
+ if (n == -1) {
+ if(stdoutatty) {
+ if (size > 0)
+ fprintf (stderr, "\r%s: 100%%", s);
+ else
+ fprintf (stderr, "\r%s: %qd Kbytes", s, (quad_t)bytes/1024);
+ }
+ bytes -= bytestart;
+ d = t.tv_sec + t.tv_usec/1.e6 - t_start.tv_sec - t_start.tv_usec/1.e6;
+ fprintf (stderr, "\n%qd bytes transfered in %.1f seconds",
+ (quad_t)bytes, d);
+ d = bytes/d;
+ if (d < 1000)
+ fprintf (stderr, " (%.0f bytes/s)\n", d);
+ else {
+ d /=1024;
+ fprintf (stderr, " (%.2f kB/s)\n", d);
+ }
+ free(s);
+ init = 0;
+ return;
+ }
+ bytes += n;
+ d = t.tv_sec + t.tv_usec/1.e6 - t0.tv_sec - t0.tv_usec/1.e6;
+ if (d < 5) /* display every 5 sec. */
+ return;
+ t0 = t;
+ pr++;
+ if(stdoutatty) {
+ if (size > 1000000)
+ fprintf (stderr, "\r%s: %2qd%%", s, (quad_t)bytes/(size/100));
+ else if (size > 0)
+ fprintf (stderr, "\r%s: %2qd%%", s, (quad_t)100*bytes/size);
+ else
+ fprintf (stderr, "\r%s: %qd kB", s, (quad_t)bytes/1024);
+ }
+}
+
diff --git a/usr.bin/fetch/uri.c b/usr.bin/fetch/uri.c
new file mode 100644
index 0000000..95d4c91
--- /dev/null
+++ b/usr.bin/fetch/uri.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright 1997 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$
+ */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "fetch.h"
+
+struct uri_scheme *schemes[] = {
+ &http_scheme, &ftp_scheme, &file_scheme, 0
+};
+
+static struct uri_scheme *
+find_scheme(const char *name)
+{
+ int i;
+
+ for (i = 0; schemes[i]; i++) {
+ if (strcasecmp(schemes[i]->sc_name, name) == 0)
+ return schemes[i];
+ }
+ return 0;
+}
+
+void
+init_schemes(void)
+{
+ int i;
+ char schemebuf[32];
+ const char *s, *t;
+ struct uri_scheme *scp;
+
+ for (i = 0; schemes[i]; i++) {
+ if (getenv(schemes[i]->sc_proxy_envar) != 0)
+ schemes[i]->sc_can_proxy = 1;
+ }
+
+ for (i = 0; schemes[i]; i++) {
+ s = schemes[i]->sc_proxy_by;
+ while (s && *s) {
+ t = strchr(s, ',');
+ if (t) {
+ schemebuf[0] = '\0';
+ strncat(schemebuf, s, t - s);
+ s = t + 1;
+ } else {
+ strcpy(schemebuf, s);
+ s = 0;
+ }
+ scp = find_scheme(schemebuf);
+ if (scp && scp->sc_can_proxy) {
+ schemes[i]->sc_proxyproto = scp;
+ break;
+ }
+ }
+ }
+}
+
+int
+parse_uri(struct fetch_state *fs, const char *uri)
+{
+ const char *colon, *slash;
+ char *scheme;
+ struct uri_scheme *scp;
+
+ fs->fs_status = "parsing URI";
+ colon = strchr(uri, ':');
+ slash = strchr(uri, '/');
+ if (!colon || !slash || slash < colon) {
+ warnx("%s: an absolute URI is required", uri);
+ return EX_USAGE;
+ }
+
+ scheme = alloca(colon - uri + 1);
+ scheme[0] = '\0';
+ strncat(scheme, uri, colon - uri);
+ scp = find_scheme(scheme);
+
+ if (scp == 0) {
+ warnx("%s: unknown URI scheme", scheme);
+ return EX_USAGE;
+ }
+ if (scp->sc_proxyproto)
+ return scp->sc_proxyproto->sc_proxy_parse(fs, uri);
+ else
+ return scp->sc_parse(fs, uri);
+}
+
diff --git a/usr.bin/fetch/util.c b/usr.bin/fetch/util.c
new file mode 100644
index 0000000..49c1108
--- /dev/null
+++ b/usr.bin/fetch/util.c
@@ -0,0 +1,332 @@
+/*-
+ * Copyright 1997 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: util.c,v 1.4 1997/02/07 17:55:01 wollman Exp $
+ */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <time.h> /* for time() */
+#include <unistd.h>
+
+#include <sys/time.h> /* for struct timeval */
+
+#include "fetch.h"
+
+
+/* Signal handling functions */
+
+/*
+ * If this were Scheme we could make this variable private to just these two
+ * functions...
+ */
+static struct sigaction oldalrm;
+
+void
+setup_sigalrm(void)
+{
+ struct sigaction catch;
+
+ sigemptyset(&catch.sa_mask);
+ sigaddset(&catch.sa_mask, SIGHUP);
+ sigaddset(&catch.sa_mask, SIGINT);
+ sigaddset(&catch.sa_mask, SIGQUIT);
+ sigaddset(&catch.sa_mask, SIGTERM);
+ sigaddset(&catch.sa_mask, SIGALRM);
+ catch.sa_handler = catchsig;
+ catch.sa_flags = 0;
+
+ sigaction(SIGALRM, &catch, &oldalrm);
+}
+
+void
+unsetup_sigalrm(void)
+{
+ sigaction(SIGALRM, &oldalrm, 0);
+}
+
+
+/* File-handling functions */
+
+/*
+ * Set the last-modified time of the output file to be that returned by
+ * the server.
+ */
+void
+adjmodtime(struct fetch_state *fs)
+{
+ struct timeval tv[2];
+
+ /* XXX - not strictly correct, since (time_t)-1 does not have to be
+ > 0. This also catches some of the other routines which erroneously
+ return 0 for invalid times rather than -1. */
+ if (!fs->fs_newtime && fs->fs_modtime > 0) {
+ tv[0].tv_usec = tv[1].tv_usec = 0;
+ time(&tv[0].tv_sec);
+ tv[1].tv_sec = fs->fs_modtime;
+ utimes(fs->fs_outputfile, tv);
+ }
+}
+
+/*
+ * Delete the file when exiting on error, if it is not `precious'.
+ */
+void
+rm(struct fetch_state *fs)
+{
+ if (!(fs->fs_outputfile[0] == '-' && fs->fs_outputfile[1] == '\0')) {
+ if (!fs->fs_restart && !fs->fs_mirror && !fs->fs_precious)
+ unlink(fs->fs_outputfile);
+ else
+ adjmodtime(fs);
+ }
+}
+
+
+/* String-handling and -parsing functions */
+
+/*
+ * Undo the standard %-sign encoding in URIs (e.g., `%2f' -> `/'). This
+ * must be done after the URI is parsed, since the principal purpose of
+ * the encoding is to hide characters which would otherwise be significant
+ * to the parser (like `/').
+ */
+char *
+percent_decode(const char *uri)
+{
+ char *rv, *s;
+
+ rv = s = safe_malloc(strlen(uri) + 1);
+
+ while (*uri) {
+ if (*uri == '%' && uri[1]
+ && isxdigit(uri[1]) && isxdigit(uri[2])) {
+ int c;
+ static char buf[] = "xx";
+
+ buf[0] = uri[1];
+ buf[1] = uri[2];
+ sscanf(buf, "%x", &c);
+ uri += 3;
+ *s++ = c;
+ } else {
+ *s++ = *uri++;
+ }
+ }
+ *s = '\0';
+ return rv;
+}
+
+/*
+ * Decode a standard host:port string into its constituents, allocating
+ * memory for a new copy of the host part.
+ */
+int
+parse_host_port(const char *s, char **hostname, int *port)
+{
+ const char *colon;
+ char *ep;
+ unsigned long ul;
+
+ colon = strchr(s, ':');
+ if (colon != 0) {
+ errno = 0;
+ ul = strtoul(colon + 1, &ep, 10);
+ if (*ep != '\0' || colon[1] == '\0' || errno != 0
+ || ul < 1 || ul > 65534) {
+ warnx("`%s': invalid port number", colon + 1);
+ return EX_USAGE;
+ }
+
+ *hostname = safe_strndup(s, colon - s);
+ *port = ul;
+ } else {
+ *hostname = safe_strdup(s);
+ }
+ return 0;
+}
+
+/*
+ * safe_malloc is like malloc, but aborts on error.
+ */
+void *
+safe_malloc(size_t len)
+{
+ void *rv;
+
+ rv = malloc(len);
+ if (rv == 0)
+ err(EX_OSERR, "malloc(%qu)", (u_quad_t)len);
+ return rv;
+}
+
+/*
+ * safe_strdup is like strdup, but aborts on error.
+ */
+char *
+safe_strdup(const char *orig)
+{
+ char *s;
+
+ s = safe_malloc(strlen(orig) + 1);
+ strcpy(s, orig);
+ return s;
+}
+
+/*
+ * safe_strndup is like safe_strdup, but copies at most `len'
+ * characters from `orig'.
+ */
+char *
+safe_strndup(const char *orig, size_t len)
+{
+ char *s;
+
+ s = safe_malloc(len + 1);
+ s[0] = '\0';
+ strncat(s, orig, len);
+ return s;
+}
+
+/*
+ * Implement the `base64' encoding as described in RFC 1521.
+ */
+static const char base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+char *
+to_base64(const unsigned char *buf, size_t len)
+{
+ char *s, *rv;
+ unsigned tmp;
+
+ s = safe_malloc((4 * (len + 1)) / 3 + 1);
+
+ rv = s;
+ while (len >= 3) {
+ tmp = buf[0] << 16 | buf[1] << 8 | buf[2];
+ s[0] = base64[tmp >> 18];
+ s[1] = base64[(tmp >> 12) & 077];
+ s[2] = base64[(tmp >> 6) & 077];
+ s[3] = base64[tmp & 077];
+ len -= 3;
+ buf += 3;
+ s += 4;
+ }
+
+ /* RFC 1521 enumerates these three possibilities... */
+ switch(len) {
+ case 2:
+ tmp = buf[0] << 16 | buf[1] << 8;
+ s[0] = base64[(tmp >> 18) & 077];
+ s[1] = base64[(tmp >> 12) & 077];
+ s[2] = base64[(tmp >> 6) & 077];
+ s[3] = '=';
+ s[4] = '\0';
+ break;
+ case 1:
+ tmp = buf[0] << 16;
+ s[0] = base64[(tmp >> 18) & 077];
+ s[1] = base64[(tmp >> 12) & 077];
+ s[2] = s[3] = '=';
+ s[4] = '\0';
+ break;
+ case 0:
+ s[0] = '\0';
+ break;
+ }
+
+ return rv;
+}
+
+int
+from_base64(const char *orig, unsigned char *buf, size_t *lenp)
+{
+ int len, len2;
+ const char *equals;
+ unsigned tmp;
+
+ len = strlen(orig);
+ while (isspace(orig[len - 1]))
+ len--;
+
+ if (len % 4)
+ return -1;
+
+ len2 = 3 * (len / 4);
+ equals = strchr(orig, '=');
+ if (equals != 0) {
+ if (equals[1] == '=')
+ len2 -= 2;
+ else
+ len2 -= 1;
+ }
+
+ /* Now the length is len2 is the actual length of the original. */
+ if (len2 > *lenp)
+ return -1;
+ *lenp = len2;
+
+ while (len > 0) {
+ int i;
+ const char *off;
+ int forget;
+
+ tmp = 0;
+ forget = 0;
+ for (i = 0; i < 4; i++) {
+ if (orig[i] == '=') {
+ off = base64;
+ forget++;
+ } else {
+ off = strchr(base64, orig[i]);
+ }
+ if (off == 0)
+ return -1;
+ tmp = (tmp << 6) | (off - base64);
+ }
+
+ buf[0] = (tmp >> 16) & 0xff;
+ if (forget < 2)
+ buf[1] = (tmp >> 8) & 0xff;
+ if (forget < 1)
+ buf[2] = (tmp >> 8) & 0xff;
+ len -= 4;
+ orig += 4;
+ buf += 3 - forget;
+ }
+ return 0;
+}
diff --git a/usr.bin/file/LEGAL.NOTICE b/usr.bin/file/LEGAL.NOTICE
new file mode 100644
index 0000000..122c5f6
--- /dev/null
+++ b/usr.bin/file/LEGAL.NOTICE
@@ -0,0 +1,31 @@
+Copyright (c) Ian F. Darwin 1986, 1987, 1989, 1990, 1991, 1992, 1994, 1995.
+Software written by Ian F. Darwin and others; maintained by Christos Zoulas.
+$Id$
+
+This software (or derivative software) may not be made subject to any
+license which denies anyone permission to alter it and redistribute it
+freely. Derivative software must also still fall under this license.
+
+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.
+ Derivative works must also be marked as such, and credits must appear
+ in the documentation.
+
+4. This notice may not be removed or altered.
+
+
diff --git a/usr.bin/file/MAINT b/usr.bin/file/MAINT
new file mode 100644
index 0000000..2b91576
--- /dev/null
+++ b/usr.bin/file/MAINT
@@ -0,0 +1,33 @@
+$Id$
+
+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..0b6d955
--- /dev/null
+++ b/usr.bin/file/Magdir/Localstuff
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# Localstuff: file(1) magic for locally observed files
+#
+# $Id$
+# 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..69cf4b4
--- /dev/null
+++ b/usr.bin/file/Magdir/alliant
@@ -0,0 +1,17 @@
+
+#------------------------------------------------------------------------------
+# alliant: file(1) magic for 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/alpha b/usr.bin/file/Magdir/alpha
new file mode 100644
index 0000000..42e1917
--- /dev/null
+++ b/usr.bin/file/Magdir/alpha
@@ -0,0 +1,21 @@
+#------------------------------------------------------------------------------
+# alpha architecture description
+#
+
+0 leshort 0603 COFF format alpha
+>22 leshort&030000 !020000 executable
+>24 leshort 0410 pure
+>24 leshort 0413 paged
+>22 leshort&020000 !0 dynamically linked
+>16 lelong !0 not stripped
+>16 lelong 0 stripped
+>22 leshort&030000 020000 shared library
+>24 leshort 0407 object
+>27 byte x - version %d
+>26 byte x .%d
+>28 byte x -%d
+
+# Basic recognition of OSF/1 core dumps - Mike Bremford <mike@opac.bl.uk>
+#
+0 string Core\001 COFF format core dump (OSF/1)
+>24 string >\0 generated by '%s'
diff --git a/usr.bin/file/Magdir/amanda b/usr.bin/file/Magdir/amanda
new file mode 100644
index 0000000..57c4359
--- /dev/null
+++ b/usr.bin/file/Magdir/amanda
@@ -0,0 +1,7 @@
+#------------------------------------------------------------------------------
+# amanda: file(1) magic for amanda file format
+#
+0 string AMANDA:\ TAPESTART\ DATE AMANDA dump header file,
+>23 string X
+>>25 string >\ Unused %s
+>23 string >\ DATE %s
diff --git a/usr.bin/file/Magdir/amigaos b/usr.bin/file/Magdir/amigaos
new file mode 100644
index 0000000..6073936
--- /dev/null
+++ b/usr.bin/file/Magdir/amigaos
@@ -0,0 +1,10 @@
+#------------------------------------------------------------------------------
+# amigaos: file(1) magic for AmigaOS binary formats:
+
+#
+# From ignatios@cs.uni-bonn.de (Ignatios Souvatzis)
+# Some formats are still missing: AmigaOS special IFF's, e.g.: FORM....CTLG
+# (the others should be seperate, anyway)
+#
+0 belong 0x000003f3 AmigaOS loadseg()ble executable/binary
+0 belong 0x000003e7 AmigaOS object/library data
diff --git a/usr.bin/file/Magdir/animation b/usr.bin/file/Magdir/animation
new file mode 100644
index 0000000..18ae9da
--- /dev/null
+++ b/usr.bin/file/Magdir/animation
@@ -0,0 +1,58 @@
+
+#------------------------------------------------------------------------------
+# animation: file(1) magic for animation/movie formats
+#
+# animation formats
+# MPEG, FLI, DL originally from vax@ccwf.cc.utexas.edu (VaX#n8)
+# FLC, SGI, Apple originally from Daniel Quinlan (quinlan@yggdrasil.com)
+
+# MPEG animation format
+0 belong 0x000001b3 Mpeg video stream data
+#>4 beshort&0xfff0 x (%d x
+#>5 beshort&0x0fff x %d)
+0 belong 0x000001ba Mpeg system stream data
+0 beshort&0xfff0 0xfff0 Mpeg audio stream data
+
+# FLI animation format
+4 leshort 0xAF11 FLI file
+>6 leshort x - %d frames,
+>8 leshort x width=%d pixels,
+>10 leshort x height=%d pixels,
+>12 leshort x depth=%d,
+>16 leshort x ticks/frame=%d
+# FLC animation format
+4 leshort 0xAF12 FLC file
+>6 leshort x - %d frames
+>8 leshort x width=%d pixels,
+>10 leshort x height=%d pixels,
+>12 leshort x depth=%d,
+>16 leshort x ticks/frame=%d
+
+# DL animation format
+# XXX - collision with most `mips' magic
+#
+# I couldn't find a real magic number for these, however, this
+# -appears- to work. Note that it might catch other files, too, so be
+# careful!
+#
+# Note that title and author appear in the two 20-byte chunks
+# at decimal offsets 2 and 22, respectively, but they are XOR'ed with
+# 255 (hex FF)! The DL format is really bad.
+#
+#0 byte 1 DL version 1, medium format (160x100, 4 images/screen)
+#>42 byte x - %d screens,
+#>43 byte x %d commands
+#0 byte 2 DL version 2
+#>1 byte 1 - large format (320x200,1 image/screen),
+#>1 byte 2 - medium format (160x100,4 images/screen),
+#>1 byte >2 - unknown format,
+#>42 byte x %d screens,
+#>43 byte x %d commands
+# Based on empirical evidence, DL version 3 have several nulls following the
+# \003. Most of them start with non-null values at hex offset 0x34 or so.
+#0 string \3\0\0\0\0\0\0\0\0\0\0\0 DL version 3
+
+# SGI and Apple formats
+0 string MOVI Silicon Graphics movie file
+4 string moov Apple QuickTime movie file (moov)
+4 string mdat Apple QuickTime movie file (mdat)
diff --git a/usr.bin/file/Magdir/apl b/usr.bin/file/Magdir/apl
new file mode 100644
index 0000000..0400431
--- /dev/null
+++ b/usr.bin/file/Magdir/apl
@@ -0,0 +1,6 @@
+
+#------------------------------------------------------------------------------
+# apl: file(1) magic for APL (see also "pdp" and "vax" for other APL
+# workspaces)
+#
+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..a6492b7
--- /dev/null
+++ b/usr.bin/file/Magdir/apple
@@ -0,0 +1,10 @@
+
+#------------------------------------------------------------------------------
+# apple: file(1) magic for 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..f266b19
--- /dev/null
+++ b/usr.bin/file/Magdir/archive
@@ -0,0 +1,210 @@
+
+#------------------------------------------------------------------------------
+# archive: file(1) magic for archive formats (see also "msdos" for self-
+# extracting compressed archives)
+#
+# cpio, ar, arc, arj, hpack, lha/lharc, rar, squish, uc2, zip, zoo, etc.
+# pre-POSIX "tar" archives are handled in the C code.
+
+# POSIX tar archives
+257 string ustar\0 POSIX tar archive
+257 string ustar\040\040\0 GNU tar archive
+
+# cpio archives
+#
+# Yes, the top 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)
+
+# other archives
+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
+
+# MIPS archive (needs to go first)
+#
+0 string !<arch>\n__________E MIPS archive
+>20 string U with MIPS Ucode members
+>21 string L with MIPSEL members
+>21 string B with MIPSEB members
+>19 string L and an EL hash table
+>19 string B and an EB hash table
+>22 string X -- out of date
+
+0 string -h- Software Tools format archive text
+
+#
+# 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 SVR1 archives, we have:
+#
+# 0 string \<ar> System V Release 1 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
+>8 string debian-split part of multipart Debian package
+>8 string debian-binary Debian binary package
+>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 belong 0x65ff0000 VAX 3.0 archive
+0 belong 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
+#
+# From "pdp" (but why a 4-byte quantity?)
+#
+0 lelong 0x39bed PDP-11 old archive
+0 lelong 0x39bee PDP-11 4.0 archive
+
+# ARC archiver, from Daniel Quinlan (quinlan@yggdrasil.com)
+#
+# The first byte is the magic (0x1a), byte 2 is the compression type for
+# the first file (0x01 through 0x09), and bytes 3 to 15 are the MS-DOS
+# filename of the first file (null terminated). Since some types collide
+# we only test some types on basis of frequency: 0x08 (83%), 0x09 (5%),
+# 0x02 (5%), 0x03 (3%), 0x04 (2%), 0x06 (2%). 0x01 collides with terminfo.
+0 lelong&0x8080ffff 0x0000081a ARC archive data, dynamic LZW
+0 lelong&0x8080ffff 0x0000091a ARC archive data, squashed
+0 lelong&0x8080ffff 0x0000021a ARC archive data, uncompressed
+0 lelong&0x8080ffff 0x0000031a ARC archive data, packed
+0 lelong&0x8080ffff 0x0000041a ARC archive data, squeezed
+0 lelong&0x8080ffff 0x0000061a ARC archive data, crunched
+
+# Acorn archive formats (Disaster prone simpleton, m91dps@ecs.ox.ac.uk)
+# I can't create either SPARK or ArcFS archives so I have not tested this stuff
+# [GRR: the original entries collide with ARC, above; replaced with combined
+# version (not tested)]
+#0 byte 0x1a RISC OS archive
+#>1 string archive (ArcFS format)
+0 string \032archive RISC OS archive (ArcFS format)
+
+# ARJ archiver (jason@jarthur.Claremont.EDU)
+0 leshort 0xea60 ARJ archive data
+>5 byte x \b, v%d,
+>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]
+
+# HA archiver (Greg Roelofs, newt@uchicago.edu)
+# This is a really bad format. A file containing HAWAII will match this...
+#0 string HA HA archive data,
+#>2 leshort =1 1 file,
+#>2 leshort >1 %u files,
+#>4 byte&0x0f =0 first is type CPY
+#>4 byte&0x0f =1 first is type ASC
+#>4 byte&0x0f =2 first is type HSC
+#>4 byte&0x0f =0x0e first is type DIR
+#>4 byte&0x0f =0x0f first is type SPECIAL
+
+# HPACK archiver (Peter Gutmann, pgut1@cs.aukuni.ac.nz)
+0 string HPAK HPACK archive data
+
+# JAM Archive volume format, by Dmitry.Kohmanyuk@UA.net
+0 string \351,\001JAM\ JAM archive,
+>7 string >\0 version %.4s
+>0x26 byte =0x27 -
+>>0x2b string >\0 label %.11s,
+>>0x27 lelong x serial %08x,
+>>0x36 string >\0 fstype %.8s
+
+# LHARC/LHA archiver (Greg Roelofs, newt@uchicago.edu)
+2 string -lh0- LHarc 1.x archive data [lh0]
+2 string -lh1- LHarc 1.x archive data [lh1]
+2 string -lz4- LHarc 1.x archive data [lz4]
+2 string -lz5- LHarc 1.x archive data [lz5]
+# [never seen any but the last; -lh4- reported in comp.compression:]
+2 string -lzs- LHa 2.x? archive data [lzs]
+2 string -lh - LHa 2.x? archive data [lh ]
+2 string -lhd- LHa 2.x? archive data [lhd]
+2 string -lh2- LHa 2.x? archive data [lh2]
+2 string -lh3- LHa 2.x? archive data [lh3]
+2 string -lh4- LHa (2.x) archive data [lh4]
+2 string -lh5- LHa (2.x) archive data [lh5]
+>20 byte x - header level %d
+
+# RAR archiver (Greg Roelofs, newt@uchicago.edu)
+0 string Rar! RAR archive data
+
+# SQUISH archiver (Greg Roelofs, newt@uchicago.edu)
+0 string SQSH squished archive data (Acorn RISCOS)
+
+# UC2 archiver (Greg Roelofs, newt@uchicago.edu)
+# I can't figure out the self-extracting form of these buggers...
+0 string UC2\x1a UC2 archive data
+
+# ZIP archives (Greg Roelofs, c/o zip-bugs@wkuvx1.wku.edu)
+0 string PK\003\004 Zip archive data
+>4 byte 0x09 \b, at least v0.9 to extract
+>4 byte 0x0a \b, at least v1.0 to extract
+>4 byte 0x0b \b, at least v1.1 to extract
+>4 byte 0x14 \b, at least v2.0 to extract
+
+# Zoo archiver
+20 lelong 0xfdc4a7dc Zoo archive data
+>4 byte >48 \b, v%c.
+>>6 byte >47 \b%c
+>>>7 byte >47 \b%c
+>32 byte >0 \b, modify: v%d
+>>33 byte x \b.%d+
+>42 lelong 0xfdc4a7dc \b,
+>>70 byte >0 extract: v%d
+>>>71 byte x \b.%d+
+
+# Shell archives
+10 string #\ This\ is\ a\ shell\ archive shell archive text
diff --git a/usr.bin/file/Magdir/asterix b/usr.bin/file/Magdir/asterix
new file mode 100644
index 0000000..d89504a
--- /dev/null
+++ b/usr.bin/file/Magdir/asterix
@@ -0,0 +1,17 @@
+
+#------------------------------------------------------------------------------
+# asterix: file(1) magic for Aster*x; SunOS 5.5.1 gave the 4-character
+# strings as "long" - we assume they're just strings:
+# From: guy@netapp.com (Guy Harris)
+#
+0 string *STA Aster*x
+>7 string WORD Words Document
+>7 string GRAP Graphic
+>7 string SPRE Spreadsheet
+>7 string MACR Macro
+0 string 2278 Aster*x Version 2
+>29 byte 0x36 Words Document
+>29 byte 0x35 Graphic
+>29 byte 0x32 Spreadsheet
+>29 byte 0x38 Macro
+
diff --git a/usr.bin/file/Magdir/att3b b/usr.bin/file/Magdir/att3b
new file mode 100644
index 0000000..7723a7f
--- /dev/null
+++ b/usr.bin/file/Magdir/att3b
@@ -0,0 +1,39 @@
+
+#------------------------------------------------------------------------------
+# att3b: file(1) magic for 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..5433557
--- /dev/null
+++ b/usr.bin/file/Magdir/audio
@@ -0,0 +1,96 @@
+
+#------------------------------------------------------------------------------
+# audio: file(1) magic for sound formats (see also "iff")
+#
+# Jan Nicolai Langfeldt (janl@ifi.uio.no), Dan Quinlan (quinlan@yggdrasil.com),
+# and others
+#
+
+# Sun/NeXT audio data
+0 string .snd Sun/NeXT audio data:
+>12 belong 1 8-bit ISDN u-law,
+>12 belong 2 8-bit linear PCM [REF-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 IEEE floating point,
+>12 belong 7 64-bit IEEE floating point,
+>12 belong 23 8-bit ISDN u-law compressed (CCITT G.721 ADPCM voice data encoding),
+>12 belong 24 compressed (8-bit G.722 ADPCM)
+>12 belong 25 compressed (3-bit G.723 ADPCM),
+>12 belong 26 compressed (5-bit G.723 ADPCM),
+>12 belong 27 8-bit A-law,
+>20 belong 1 mono,
+>20 belong 2 stereo,
+>20 belong 4 quad,
+>16 belong >0 %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
+0 lelong 0x0064732E DEC audio data:
+>12 lelong 1 8-bit ISDN u-law,
+>12 lelong 2 8-bit linear PCM [REF-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 IEEE floating point,
+>12 lelong 7 64-bit IEEE floating point,
+>12 lelong 23 8-bit ISDN u-law compressed (CCITT G.721 ADPCM voice data encoding),
+>20 lelong 1 mono,
+>20 lelong 2 stereo,
+>20 lelong 4 quad,
+>16 lelong >0 %d Hz
+
+# Creative Labs AUDIO stuff
+0 string MThd Standard MIDI data
+>9 byte >0 (format %d)
+>11 byte >1 using %d channels
+0 string CTMF Creative Music (CMF) data
+0 string SBI SoundBlaster instrument data
+0 string Creative\ Voice\ File Creative Labs voice data
+# is this next line right? it came this way...
+>19 byte 0x1A
+>23 byte >0 - version %d
+>22 byte >0 \b.%d
+
+# first entry is also the string "NTRK"
+0 belong 0x4e54524b MultiTrack sound data
+>4 belong x - version %ld
+
+# Microsoft WAVE format (*.wav)
+0 string RIFF Microsoft RIFF
+>8 string WAVE \b, WAVE audio data
+>>34 leshort >0 \b, %d bit
+>>22 leshort =1 \b, mono
+>>22 leshort =2 \b, stereo
+>>22 leshort >2 \b, %d channels
+>>24 lelong >0 %d Hz
+# AVI == Audio Video Interleave
+>8 string AVI\ \b, AVI data
+
+# Extended MOD format (*.emd) (Greg Roelofs, newt@uchicago.edu); NOT TESTED
+# [based on posting 940824 by "Dirk/Elastik", husberg@lehtori.cc.tut.fi]
+0 string EMOD Extended MOD sound data,
+>4 byte&0xf0 x version %d
+>4 byte&0x0f x \b.%d,
+>45 byte x %d instruments
+>83 byte 0 (module)
+>83 byte 1 (song)
+
+# Real Audio (Magic .ra\0375)
+0 belong 0x2e7261fd realaudio sound file
+
+# MTM/669/FAR/S3M/ULT/XM format checking [Aaron Eppert, aeppert@dialin.ind.net]
+# Oct 31, 1995
+0 string MTM MultiTracker Module sound file
+0 string if Composer 669 Module sound data
+0 string FAR Module sound data
+0 string MAS_U ULT(imate) Module sound data
+0x2c string SCRM ScreamTracker III Module sound data
+0 string Extended Module Extended Module sound data
+
+# Gravis UltraSound patches
+# From <ache@nagual.ru>
+
+0 string GF1PATCH110\0ID#000002\0 GUS patch
+0 string GF1PATCH100\0ID#000002\0 Old GUS patch
diff --git a/usr.bin/file/Magdir/blit b/usr.bin/file/Magdir/blit
new file mode 100644
index 0000000..7a470ed
--- /dev/null
+++ b/usr.bin/file/Magdir/blit
@@ -0,0 +1,19 @@
+
+#------------------------------------------------------------------------------
+# blit: file(1) magic for 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..2e3b646
--- /dev/null
+++ b/usr.bin/file/Magdir/bsdi
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# bsdi: file(1) magic for BSD/OS (from BSDI) objects
+#
+0 lelong 000000314 BSD/OS i386 compact demand paged executable
+>16 lelong >0 not stripped
+>32 byte 0x6a (uses shared libs)
diff --git a/usr.bin/file/Magdir/bzip b/usr.bin/file/Magdir/bzip
new file mode 100644
index 0000000..97692ce7
--- /dev/null
+++ b/usr.bin/file/Magdir/bzip
@@ -0,0 +1,14 @@
+# bzip a block-sorting file compressor
+# by Julian Seward <sewardj@cs.man.ac.uk> and others
+#
+0 string BZ bzip compressed data
+>2 byte x \b, version: %c
+>3 string =1 \b, compression block size 100k
+>3 string =2 \b, compression block size 200k
+>3 string =3 \b, compression block size 300k
+>3 string =4 \b, compression block size 400k
+>3 string =5 \b, compression block size 500k
+>3 string =6 \b, compression block size 600k
+>3 string =7 \b, compression block size 700k
+>3 string =8 \b, compression block size 800k
+>3 string =9 \b, compression block size 900k
diff --git a/usr.bin/file/Magdir/c-lang b/usr.bin/file/Magdir/c-lang
new file mode 100644
index 0000000..1b01475
--- /dev/null
+++ b/usr.bin/file/Magdir/c-lang
@@ -0,0 +1,13 @@
+
+#------------------------------------------------------------------------------
+# c-lang: file(1) magic for C programs (or REXX)
+#
+
+# XPM icons (Greg Roelofs, newt@uchicago.edu)
+# if you uncomment "/*" for C/REXX below, also uncomment this entry
+#0 string /*\ XPM\ */ X pixmap image data
+
+# 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 or REXX program text
+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..ee450f5
--- /dev/null
+++ b/usr.bin/file/Magdir/chi
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# chi: file(1) magic for 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..c325cb8
--- /dev/null
+++ b/usr.bin/file/Magdir/clipper
@@ -0,0 +1,64 @@
+
+#------------------------------------------------------------------------------
+# clipper: file(1) magic for 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..8a27607
--- /dev/null
+++ b/usr.bin/file/Magdir/commands
@@ -0,0 +1,75 @@
+
+#------------------------------------------------------------------------------
+# commands: file(1) magic 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
+#
+# zsh/ash/ae/nawk/gawk magic from cameron@cs.unsw.oz.au (Cameron Simpson)
+0 string #!/usr/local/bin/zsh Paul Falstad's zsh
+0 string #!\ /usr/local/bin/zsh Paul Falstad's zsh
+0 string #!/usr/local/bin/ash Neil Brown's ash
+0 string #!\ /usr/local/bin/ash Neil Brown's ash
+0 string #!/usr/local/bin/ae Neil Brown's ae
+0 string #!\ /usr/local/bin/ae Neil Brown's ae
+0 string #!/bin/nawk new awk script text
+0 string #!\ /bin/nawk new awk script text
+0 string #!/usr/bin/nawk new awk script text
+0 string #!\ /usr/bin/nawk new awk script text
+0 string #!/usr/local/bin/nawk new awk script text
+0 string #!\ /usr/local/bin/nawk new awk script text
+0 string #!/bin/gawk GNU awk script text
+0 string #!\ /bin/gawk GNU awk script text
+0 string #!/usr/bin/gawk GNU awk script text
+0 string #!\ /usr/bin/gawk GNU awk script text
+0 string #!/usr/local/bin/gawk GNU awk script text
+0 string #!\ /usr/local/bin/gawk GNU awk script text
+#
+0 string #!/bin/awk awk commands text
+0 string #!\ /bin/awk awk commands text
+0 string #!/usr/bin/awk awk commands text
+0 string #!\ /usr/bin/awk awk commands text
+0 string BEGIN awk commands text
+
+# 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
+
+# AT&T Bell Labs' Plan 9 shell
+0 string #!/bin/rc Plan 9 rc shell script text
+0 string #!\ /bin/rc Plan 9 rc shell script text
+
+# bash shell magic, from Peter Tobias (tobias@server.et-inf.fho-emden.de)
+0 string #!/bin/bash Bourne-Again shell script text
+0 string #!\ /bin/bash Bourne-Again shell script text
+0 string #!/usr/local/bin/bash Bourne-Again shell script text
+0 string #!\ /usr/local/bin/bash Bourne-Again shell script text
+
+# generic shell magic
+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
diff --git a/usr.bin/file/Magdir/compress b/usr.bin/file/Magdir/compress
new file mode 100644
index 0000000..a797f8f
--- /dev/null
+++ b/usr.bin/file/Magdir/compress
@@ -0,0 +1,92 @@
+
+#------------------------------------------------------------------------------
+# compress: file(1) magic for pure-compression formats (no archives)
+#
+# compress, gzip, pack, compact, huf, squeeze, crunch, freeze, yabba, etc.
+#
+# 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.
+
+# standard unix compress
+0 string \037\235 compress'd data
+>2 byte&0x80 >0 block compressed
+>2 byte&0x1f x %d bits
+
+# gzip (GNU zip, not to be confused with Info-ZIP or PKWARE zip archiver)
+0 string \037\213 gzip compressed data
+>2 byte <8 \b, reserved method,
+>2 byte 8 \b, deflated,
+>3 byte &0x01 ASCII,
+>3 byte &0x02 continuation,
+>3 byte &0x04 extra field,
+>3 byte &0x08 original filename,
+>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
+
+# packed data, Huffman (minimum redundancy) codes on a byte-by-byte basis
+0 string \037\036 packed data
+>2 belong >1 \b, %d characters originally
+>2 belong =1 \b, %d character originally
+#
+# This magic number is byte-order-independent. XXX - Does that mean this
+# is big-endian, little-endian, either, or that you can't tell?
+# this short is valid for SunOS
+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
+# This string is valid for SunOS (BE) and a matching "short" is listed
+# in the Ultrix (LE) magic file.
+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)
+
+# SCO compress -H (LZH)
+0 string \037\240 SCO compress -H (LZH) data
+
+# European GSM 06.10 is a provisional standard for full-rate speech
+# transcoding, prI-ETS 300 036, which uses RPE/LTP (residual pulse
+# excitation/long term prediction) coding at 13 kbit/s.
+#
+# There's only a magic nibble (4 bits); that nibble repeats every 33
+# bytes. This isn't suited for use, but maybe we can use it someday.
+#
+# This will cause very short GSM files to be declared as data and
+# mismatches to be declared as data too!
+#0 byte&0xF0 0xd0 data
+#>33 byte&0xF0 0xd0
+#>66 byte&0xF0 0xd0
+#>99 byte&0xF0 0xd0
+#>132 byte&0xF0 0xd0 GSM 06.10 compressed audio
+
+# Bzip from ulmo@Q.Net
+0 string BZ bzip compressed data,
+>2 byte x format v. %c,
+>3 byte x block size indicator %c
+
diff --git a/usr.bin/file/Magdir/convex b/usr.bin/file/Magdir/convex
new file mode 100644
index 0000000..b1235d7
--- /dev/null
+++ b/usr.bin/file/Magdir/convex
@@ -0,0 +1,69 @@
+#------------------------------------------------------------------------------
+# convex: file(1) magic for Convex boxes
+#
+# Convexes are big-endian.
+#
+# /*\
+# * Below are the magic numbers and tests added for Convex.
+# * Added at beginning, because they are expected to be used most.
+# \*/
+0 belong 0507 Convex old-style object
+>16 belong >0 not stripped
+0 belong 0513 Convex old-style demand paged executable
+>16 belong >0 not stripped
+0 belong 0515 Convex old-style pre-paged executable
+>16 belong >0 not stripped
+0 belong 0517 Convex old-style pre-paged, non-swapped executable
+>16 belong >0 not stripped
+0 belong 0x011257 Core file
+#
+# The following are a series of dump format magic numbers. Each one
+# corresponds to a drastically different dump format. The first on is
+# the original dump format on a 4.1 BSD or earlier file system. The
+# second marks the change between the 4.1 file system and the 4.2 file
+# system. The Third marks the changing of the block size from 1K
+# to 2K to be compatible with an IDC file system. The fourth indicates
+# a dump that is dependent on Convex Storage Manager, because data in
+# secondary storage is not physically contained within the dump.
+# The restore program uses these number to determine how the data is
+# to be extracted.
+#
+24 belong =60011 dump format, 4.1 BSD or earlier
+24 belong =60012 dump format, 4.2 or 4.3 BSD without IDC
+24 belong =60013 dump format, 4.2 or 4.3 BSD (IDC compatible)
+24 belong =60014 dump format, Convex Storage Manager by-reference dump
+#
+# what follows is a bunch of bit-mask checks on the flags field of the opthdr.
+# If there is no `=' sign, assume just checking for whether the bit is set?
+#
+0 belong 0601 Convex SOFF
+>88 belong&0x000f0000 =0x00000000 c1
+>88 belong &0x00010000 c2
+>88 belong &0x00020000 c2mp
+>88 belong &0x00040000 parallel
+>88 belong &0x00080000 intrinsic
+>88 belong &0x00000001 demand paged
+>88 belong &0x00000002 pre-paged
+>88 belong &0x00000004 non-swapped
+>88 belong &0x00000008 POSIX
+#
+>84 belong &0x80000000 executable
+>84 belong &0x40000000 object
+>84 belong&0x20000000 =0 not stripped
+>84 belong&0x18000000 =0x00000000 native fpmode
+>84 belong&0x18000000 =0x10000000 ieee fpmode
+>84 belong&0x18000000 =0x18000000 undefined fpmode
+#
+0 belong 0605 Convex SOFF core
+#
+0 belong 0607 Convex SOFF checkpoint
+>88 belong&0x000f0000 =0x00000000 c1
+>88 belong &0x00010000 c2
+>88 belong &0x00020000 c2mp
+>88 belong &0x00040000 parallel
+>88 belong &0x00080000 intrinsic
+>88 belong &0x00000008 POSIX
+#
+>84 belong&0x18000000 =0x00000000 native fpmode
+>84 belong&0x18000000 =0x10000000 ieee fpmode
+>84 belong&0x18000000 =0x18000000 undefined fpmode
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/database b/usr.bin/file/Magdir/database
new file mode 100644
index 0000000..146c310
--- /dev/null
+++ b/usr.bin/file/Magdir/database
@@ -0,0 +1,38 @@
+
+#------------------------------------------------------------------------------
+# database: file(1) magic for various databases
+#
+# extracted from header/code files by Graeme Wilford (eep2gw@ee.surrey.ac.uk)
+#
+#
+# GDBM magic numbers
+# Will be maintained as part of the GDBM distribution in the future.
+# <downsj@teeny.org>
+0 belong 0x13579ace GNU dbm 1.x or ndbm database, big endian
+0 lelong 0x13579ace GNU dbm 1.x or ndbm database, little endian
+0 string GDBM GNU dbm 2.x database
+#
+0 belong 0x061561 Berkeley DB Hash file
+>4 belong >0 (Version %d,
+>8 belong 1234 Little Endian,
+>8 belong 4321 Big Endian,
+>12 belong x Bucket Size %d,
+>16 belong x Bucket Shift %d,
+>20 belong x Directory Size %d,
+>24 belong x Segment Size %d,
+>28 belong x Segment Shift %d,
+>32 belong x Overflow Point %d,
+>36 belong x Last Freed %d,
+>40 belong x Max Bucket %d,
+>44 belong x High Mask 0x%x,
+>48 belong x Low Mask 0x%x,
+>52 belong x Fill Factor %d,
+>56 belong x Number of Keys %d)
+#
+#
+0 belong 0x053162 Berkeley DB Btree file
+>4 belong >0 (Version %d,
+>8 belong x Page Size %d,
+>12 belong x Free Page %d,
+>16 belong x Number of Records %d,
+>20 belong x Flags 0x%x)
diff --git a/usr.bin/file/Magdir/diamond b/usr.bin/file/Magdir/diamond
new file mode 100644
index 0000000..1abd01e
--- /dev/null
+++ b/usr.bin/file/Magdir/diamond
@@ -0,0 +1,11 @@
+
+#------------------------------------------------------------------------------
+# diamond: file(1) magic for Diamond system
+#
+# ... 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..9e65146
--- /dev/null
+++ b/usr.bin/file/Magdir/diff
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# diff: file(1) magic for diff(1) output
+#
+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/digital b/usr.bin/file/Magdir/digital
new file mode 100644
index 0000000..f4ebbff
--- /dev/null
+++ b/usr.bin/file/Magdir/digital
@@ -0,0 +1,41 @@
+# Digital UNIX - Info
+#
+0 string ^!<arch>\n_______64E Alpha archive
+>22 string X -- out of date
+#
+# Alpha COFF Based Executables
+# The stripped stuff really needs to be an 8 byte (64 bit) compare,
+# but this works
+0 leshort 0x183 COFF format alpha
+>22 leshort&020000 &010000 sharable library,
+>22 leshort&020000 ^010000 dynamically linked,
+>24 leshort 0410 pure
+>24 leshort 0413 demand paged
+>8 lelong >0 executable or object module, not stripped
+>8 lelong 0
+>>12 lelong 0 executable or object module, stripped
+>>12 lelong >0 executable or object module, not stripped
+>27 byte >0 - version %d.
+>26 byte >0 %d-
+>28 leshort >0 %d
+#
+# The next is incomplete, we could tell more about this format,
+# but its not worth it.
+0 leshort 0x188 Alpha compressed COFF
+0 leshort 0x18f Alpha u-code object
+#
+#
+# Some other interesting Digital formats,
+0 string \377\377\177 ddis/ddif
+0 string \377\377\174 ddis/dots archive
+0 string \377\377\176 ddis/dtif table data
+0 string \033c\033 LN03 output
+0 long 04553207 X image
+#
+0 string !<PDF>!\n profiling data file
+#
+# Locale data tables (MIPS and Alpha).
+#
+0 short 0x0501 locale data table
+>6 short 0x24 for MIPS
+>6 short 0x40 for Alpha
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..628ead8
--- /dev/null
+++ b/usr.bin/file/Magdir/dump
@@ -0,0 +1,81 @@
+
+#------------------------------------------------------------------------------
+# dump: file(1) magic for 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 This dump %s,
+>8 ledate x Previous 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..58f4f36
--- /dev/null
+++ b/usr.bin/file/Magdir/elf
@@ -0,0 +1,76 @@
+
+#------------------------------------------------------------------------------
+# elf: file(1) magic for ELF executables
+#
+# We have to check the byte order flag to see what byte order all the
+# other stuff in the header is in.
+#
+# MIPS RS3000 may also be for MIPS RS2000.
+# What're the correct byte orders for the nCUBE and the Fujitsu VPP500?
+#
+# updated by Daniel Quinlan (quinlan@yggdrasil.com)
+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 no file type,
+>>16 leshort 1 relocatable,
+>>16 leshort 2 executable,
+>>16 leshort 3 shared object,
+# Core handling from Peter Tobias <tobias@server.et-inf.fho-emden.de>
+>>16 leshort 4 core file
+>>>400 lelong >0 (signal %d),
+>>16 leshort &0xff00 processor-specific,
+>>18 leshort 0 no machine,
+>>18 leshort 1 AT&T WE32100 - invalid byte order,
+>>18 leshort 2 SPARC - invalid byte order,
+>>18 leshort 3 Intel 80386,
+>>18 leshort 4 Motorola 68000 - invalid byte order,
+>>18 leshort 5 Motorola 88000 - invalid byte order,
+>>18 leshort 6 Intel 80486,
+>>18 leshort 7 Intel 80860,
+>>18 leshort 8 MIPS RS3000_BE - invalid byte order,
+>>18 leshort 9 Amdahl - invalid byte order,
+>>18 leshort 10 MIPS RS3000_LE,
+>>18 leshort 11 RS6000 - invalid byte order,
+>>18 leshort 15 PA_RISC - invalid byte order,
+>>18 leshort 16 nCUBE,
+>>18 leshort 17 VPP500,
+>>18 leshort 18 SPARC32PLUS,
+>>18 leshort 20 PowerPC,
+>>18 leshort 0x9026 Alpha,
+>>20 lelong 0 invalid version
+>>20 lelong 1 version 1
+>>36 lelong 1 MathCoPro/FPU/MAU Required
+>5 byte 2 MSB
+>>16 beshort 0 no file type,
+>>16 beshort 1 relocatable,
+>>16 beshort 2 executable,
+>>16 beshort 3 shared object,
+>>16 beshort 4 core file,
+>>>400 lelong >0 (signal %d),
+>>16 beshort &0xff00 processor-specific,
+>>18 beshort 0 no machine,
+>>18 beshort 1 AT&T WE32100,
+>>18 beshort 2 SPARC,
+>>18 beshort 3 Intel 80386 - invalid byte order,
+>>18 beshort 4 Motorola 68000,
+>>18 beshort 5 Motorola 88000,
+>>18 beshort 6 Intel 80486 - invalid byte order,
+>>18 beshort 7 Intel 80860,
+>>18 beshort 8 MIPS RS3000_BE,
+>>18 beshort 9 Amdahl,
+>>18 beshort 10 MIPS RS3000_LE - invalid byte order,
+>>18 beshort 11 RS6000,
+>>18 beshort 15 PA_RISC,
+>>18 beshort 16 nCUBE,
+>>18 beshort 17 VPP500,
+>>18 beshort 18 SPARC32PLUS,
+>>18 beshort 20 PowerPC,
+>>18 beshort 0x9026 Alpha,
+>>20 belong 0 invalid version
+>>20 belong 1 version 1
+>>36 belong 1 MathCoPro/FPU/MAU Required
+>8 string >\0 (%s)
diff --git a/usr.bin/file/Magdir/encore b/usr.bin/file/Magdir/encore
new file mode 100644
index 0000000..63cb5d4
--- /dev/null
+++ b/usr.bin/file/Magdir/encore
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# encore: file(1) magic for 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/figlet b/usr.bin/file/Magdir/figlet
new file mode 100644
index 0000000..ac650b1
--- /dev/null
+++ b/usr.bin/file/Magdir/figlet
@@ -0,0 +1,9 @@
+
+#------------------------------------------------------------------------------
+# figlet: file(1) magic for FIGlet fonts and controlfiles
+# From figmagic supplied with Figlet version 2.2
+#
+0 string flf FIGlet font
+>3 string >2a version %-2.2s
+0 string flc FIGlet controlfile
+>3 string >2a version %-2.2s
diff --git a/usr.bin/file/Magdir/filesystems b/usr.bin/file/Magdir/filesystems
new file mode 100644
index 0000000..125250d
--- /dev/null
+++ b/usr.bin/file/Magdir/filesystems
@@ -0,0 +1,6 @@
+
+#------------------------------------------------------------------------------
+# filesystems: file(1) magic for different filesystems
+#
+0x438 leshort 0xEF53 Linux/i386 ext2 filesystem
+0 string \366\366\366\366 PC formatted floppy with no filesystem
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..dd7907f
--- /dev/null
+++ b/usr.bin/file/Magdir/fonts
@@ -0,0 +1,26 @@
+
+#------------------------------------------------------------------------------
+# fonts: file(1) magic for font data
+#
+0 string FONT ASCII vfont text
+0 short 0436 Berkeley vfont data
+0 short 017001 byte-swapped Berkeley vfont data
+
+# PostScript fonts (must precede "printer" entries), quinlan@yggdrasil.com
+0 string %!PS-AdobeFont-1.0 PostScript Type 1 font text
+>20 string >\0 (%s)
+6 string %!PS-AdobeFont-1.0 PostScript Type 1 font program data
+
+# X11 font files in SNF (Server Natural Format) format
+0 belong 00000004 X11 SNF font data, MSB first
+0 lelong 00000004 X11 SNF font data, LSB first
+
+# X11 Bitmap Distribution Format, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 string STARTFONT\040 X11 BDF font text
+
+# X11 fonts, from Daniel Quinlan (quinlan@yggdrasil.com)
+# PCF must come before SGI additions ("MIPSEL MIPS-II COFF" collides)
+0 string \001fcp X11 Portable Compiled Font data
+>12 byte 0x02 \b, LSB first
+>12 byte 0x0a \b, MSB first
+0 string D1.0\015 X11 Speedo font data
diff --git a/usr.bin/file/Magdir/frame b/usr.bin/file/Magdir/frame
new file mode 100644
index 0000000..e2c10f1
--- /dev/null
+++ b/usr.bin/file/Magdir/frame
@@ -0,0 +1,37 @@
+
+#------------------------------------------------------------------------------
+# frame: file(1) magic 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 5.0 (5.0
+>11 string 4.0 (4.0
+>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 (ASCII) file
+>9 string 4.0 (4.0)
+>9 string 3.0 (3.0)
+>9 string 2.0 (2.0)
+>9 string 1.0 (1.x)
+0 string \<MakerDictionary FrameMaker Dictionary text
+>17 string 3.0 (3.0)
+>17 string 2.0 (2.0)
+>17 string 1.0 (1.x)
+0 string \<MakerScreenFont FrameMaker Font file
+>17 string 1.01 (%s)
+0 string \<MML FrameMaker MML file
+0 string \<BookFile 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)
+# XXX - this book entry should be verified, if you find one, uncomment this
+#0 string \<Book\ FrameMaker Book (ASCII) file
+#>6 string 3.0 (3.0)
+#>6 string 2.0 (2.0)
+#>6 string 1.0 (1.0)
+0 string \<Maker Intermediate Print File FrameMaker IPL file
diff --git a/usr.bin/file/Magdir/freebsd b/usr.bin/file/Magdir/freebsd
new file mode 100644
index 0000000..2370c25
--- /dev/null
+++ b/usr.bin/file/Magdir/freebsd
@@ -0,0 +1,130 @@
+
+#------------------------------------------------------------------------------
+# freebsd: file(1) magic for FreeBSD objects
+#
+# All new-style FreeBSD magic numbers are in host byte order (i.e.,
+# little-endian on x86).
+#
+# XXX - this comes from the file "freebsd" in a recent FreeBSD version of
+# "file"; it, and the NetBSD stuff in "netbsd", appear to use different
+# schemes for distinguishing between executable images, shared libraries,
+# and object files.
+#
+# FreeBSD says:
+#
+# Regardless of whether it's pure, demand-paged, or none of the
+# above:
+#
+# if the entry point is < 4096, then it's a shared library if
+# the "has run-time loader information" bit is set, and is
+# position-independent if the "is position-independent" bit
+# is set;
+#
+# if the entry point is >= 4096 (or >4095, same thing), then it's
+# an executable, and is dynamically-linked if the "has run-time
+# loader information" bit is set.
+#
+# On x86, NetBSD says:
+#
+# If it's neither pure nor demand-paged:
+#
+# if it has the "has run-time loader information" bit set, it's
+# a dynamically-linked executable;
+#
+# if it doesn't have that bit set, then:
+#
+# if it has the "is position-independent" bit set, it's
+# position-independent;
+#
+# if the entry point is non-zero, it's an executable, otherwise
+# it's an object file.
+#
+# If it's pure:
+#
+# if it has the "has run-time loader information" bit set, it's
+# a dynamically-linked executable, otherwise it's just an
+# executable.
+#
+# If it's demand-paged:
+#
+# if it has the "has run-time loader information" bit set,
+# then:
+#
+# if the entry point is < 4096, it's a shared library;
+#
+# if the entry point is = 4096 or > 4096 (i.e., >= 4096),
+# it's a dynamically-linked executable);
+#
+# if it doesn't have the "has run-time loader information" bit
+# set, then it's just an executable.
+#
+# (On non-x86, NetBSD does much the same thing, except that it uses
+# 8192 on 68K - except for "68k4k", which is presumably "68K with 4K
+# pages - SPARC, and MIPS, presumably because Sun-3's and Sun-4's
+# had 8K pages; dunno about MIPS.)
+#
+# I suspect the two will differ only in perverse and uninteresting cases
+# ("shared" libraries that aren't demand-paged and whose pages probably
+# won't actually be shared, executables with entry points <4096).
+#
+# I leave it to those more familiar with FreeBSD and NetBSD to figure out
+# what the right answer is (although using ">4095", FreeBSD-style, is
+# probably better than separately checking for "=4096" and ">4096",
+# NetBSD-style). (The old "netbsd" file analyzed FreeBSD demand paged
+# executables using the NetBSD technique.)
+#
+0 lelong&0377777777 041400407 FreeBSD/i386
+>20 lelong <4096
+>>3 byte&0xC0 &0x80 shared library
+>>3 byte&0xC0 0x40 PIC object
+>>3 byte&0xC0 0x00 object
+>20 lelong >4095
+>>3 byte&0x80 0x80 dynamically linked executable
+>>3 byte&0x80 0x00 executable
+>16 lelong >0 not stripped
+
+0 lelong&0377777777 041400410 FreeBSD/i386 pure
+>20 lelong <4096
+>>3 byte&0xC0 &0x80 shared library
+>>3 byte&0xC0 0x40 PIC object
+>>3 byte&0xC0 0x00 object
+>20 lelong >4095
+>>3 byte&0x80 0x80 dynamically linked executable
+>>3 byte&0x80 0x00 executable
+>16 lelong >0 not stripped
+
+0 lelong&0377777777 041400413 FreeBSD/i386 demand paged
+>20 lelong <4096
+>>3 byte&0xC0 &0x80 shared library
+>>3 byte&0xC0 0x40 PIC object
+>>3 byte&0xC0 0x00 object
+>20 lelong >4095
+>>3 byte&0x80 0x80 dynamically linked executable
+>>3 byte&0x80 0x00 executable
+>16 lelong >0 not stripped
+
+0 lelong&0377777777 041400314 FreeBSD/i386 compact demand paged
+>20 lelong <4096
+>>3 byte&0xC0 &0x80 shared library
+>>3 byte&0xC0 0x40 PIC object
+>>3 byte&0xC0 0x00 object
+>20 lelong >4095
+>>3 byte&0x80 0x80 dynamically linked executable
+>>3 byte&0x80 0x00 executable
+>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 FreeBSD/i386 a.out core file
+>1039 string >\0 from '%s'
+
+# /var/run/ld.so.hints
+# What are you laughing about?
+0 lelong 011421044151 ld.so hints file
+>4 lelong >0 (version %d)
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..e1efdbd
--- /dev/null
+++ b/usr.bin/file/Magdir/hp
@@ -0,0 +1,228 @@
+
+#------------------------------------------------------------------------------
+# hp: file(1) magic for Hewlett Packard machines (see also "printer")
+#
+# 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.
+#
+# 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.
+#
+# Daniel Quinlan (quinlan@yggdrasil.com): hp200 machines are 68010 based;
+# hp300 are 68020+68881 based; hp400 are also 68k. The following basic
+# HP magic is useful for reference, but using "long" magic is a better
+# practice in order to avoid collisions.
+#
+# Guy Harris (guy@netapp.com): some additions to this list came from
+# HP-UX 10.0's "/usr/include/sys/unistd.h" (68030, 68040, PA-RISC 1.1,
+# 1.2, and 2.0). The 1.2 and 2.0 stuff isn't in the HP-UX 10.0
+# "/etc/magic", though, except for the "archive file relocatable library"
+# stuff, and the 68030 and 68040 stuff isn't there at all - are they not
+# used in executables, or have they just not yet updated "/etc/magic"
+# completely?
+#
+# 0 beshort 200 hp200 (68010) BSD binary
+# 0 beshort 300 hp300 (68020+68881) BSD binary
+# 0 beshort 0x20c hp200/300 HP-UX binary
+# 0 beshort 0x20d hp400 (68030) HP-UX binary
+# 0 beshort 0x20e hp400 (68040?) HP-UX binary
+# 0 beshort 0x20b PA-RISC1.0 HP-UX binary
+# 0 beshort 0x210 PA-RISC1.1 HP-UX binary
+# 0 beshort 0x211 PA-RISC1.2 HP-UX binary
+# 0 beshort 0x214 PA-RISC2.0 HP-UX binary
+
+#
+# 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
+#### PA-RISC
+0 belong 0x02100106 PA-RISC1.1 relocatable object
+0 belong 0x02100107 PA-RISC1.1 executable
+>168 belong &=0x00000004 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x02100108 PA-RISC1.1 shared executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x0210010b PA-RISC1.1 demand-load executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x0210010e PA-RISC1.1 shared library
+>96 belong >0 - not stripped
+
+0 belong 0x0210010d PA-RISC1.1 dynamic load library
+>96 belong >0 - not stripped
+
+#### 800
+0 belong 0x020b0106 PA-RISC1.0 relocatable object
+
+0 belong 0x020b0107 PA-RISC1.0 executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x020b0108 PA-RISC1.0 shared executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x020b010b PA-RISC1.0 demand-load executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x020b010e PA-RISC1.0 shared library
+>96 belong >0 - not stripped
+
+0 belong 0x020b010d PA-RISC1.0 dynamic load library
+>96 belong >0 - not stripped
+
+0 belong 0x213c6172 archive file
+>68 belong 0x020b0619 - PA-RISC1.0 relocatable library
+>68 belong 0x02100619 - PA-RISC1.1 relocatable library
+>68 belong 0x02110619 - PA-RISC1.2 relocatable library
+>68 belong 0x02140619 - PA-RISC2.0 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
+# XXX - see "lif"
+#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(,);
+
+# hpBSD magic numbers
+0 beshort 200 hp200 (68010) BSD
+>2 beshort 0407 impure binary
+>2 beshort 0410 read-only binary
+>2 beshort 0413 demand paged binary
+0 beshort 300 hp300 (68020+68881) BSD
+>2 beshort 0407 impure binary
+>2 beshort 0410 read-only binary
+>2 beshort 0413 demand paged binary
+
diff --git a/usr.bin/file/Magdir/ibm370 b/usr.bin/file/Magdir/ibm370
new file mode 100644
index 0000000..8cd9da2
--- /dev/null
+++ b/usr.bin/file/Magdir/ibm370
@@ -0,0 +1,47 @@
+
+#------------------------------------------------------------------------------
+# ibm370: file(1) magic for IBM 370 and compatibles.
+#
+# "ibm370" said that 0x15d == 0535 was "ibm 370 pure executable".
+# What the heck *is* "USS/370"?
+# AIX 4.1's "/etc/magic" has
+#
+# 0 short 0535 370 sysV executable
+# >12 long >0 not stripped
+# >22 short >0 - version %d
+# >30 long >0 - 5.2 format
+# 0 short 0530 370 sysV pure executable
+# >12 long >0 not stripped
+# >22 short >0 - version %d
+# >30 long >0 - 5.2 format
+#
+# instead of the "USS/370" versions of the same magic numbers.
+#
+0 beshort 0537 370 XA sysV executable
+>12 belong >0 not stripped
+>22 beshort >0 - version %d
+>30 belong >0 - 5.2 format
+0 beshort 0532 370 XA sysV pure executable
+>12 belong >0 not stripped
+>22 beshort >0 - version %d
+>30 belong >0 - 5.2 format
+0 beshort 054001 370 sysV pure executable
+>12 belong >0 not stripped
+0 beshort 055001 370 XA sysV pure executable
+>12 belong >0 not stripped
+0 beshort 056401 370 sysV executable
+>12 belong >0 not stripped
+0 beshort 057401 370 XA sysV executable
+>12 belong >0 not stripped
+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..8e1077b
--- /dev/null
+++ b/usr.bin/file/Magdir/ibm6000
@@ -0,0 +1,17 @@
+
+#------------------------------------------------------------------------------
+# ibm6000: file(1) magic for 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 AIX message catalog
+0 belong 0x000001f9 AIX compiled 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..68d1b79
--- /dev/null
+++ b/usr.bin/file/Magdir/iff
@@ -0,0 +1,28 @@
+
+#------------------------------------------------------------------------------
+# iff: file(1) magic for Interchange File Format (see also "audio" & "images")
+#
+# Daniel Quinlan (quinlan@yggdrasil.com) -- IFF was designed by Electronic
+# Arts for file interchange. It has also been used by Apple, SGI, and
+# especially Commodore-Amiga.
+#
+# IFF files begin with an 8 byte FORM header, followed by a 4 character
+# FORM type, which is followed by the first chunk in the FORM.
+
+0 string FORM IFF data
+#>4 belong x \b, FORM is %d bytes long
+# audio formats
+>8 string AIFF \b, AIFF audio
+>8 string AIFC \b, AIFF-C compressed audio
+>8 string 8SVX \b, 8SVX 8-bit sampled sound voice
+>8 string SAMP \b, SAMP sampled audio
+# image formats
+>8 string ILBMBMHD \b, ILBM interleaved image
+>>20 beshort x \b, %d x
+>>22 beshort x %d
+>8 string RGBN \b, RGBN 12-bit RGB image
+>8 string RGB8 \b, RGB8 24-bit RGB image
+>8 string DR2D \b, DR2D 2-D object
+>8 string TDDD \b, TDDD 3-D rendering
+# other formats
+>8 string FTXT \b, FTXT formatted text
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..271b169
--- /dev/null
+++ b/usr.bin/file/Magdir/images
@@ -0,0 +1,236 @@
+
+#------------------------------------------------------------------------------
+# images: file(1) magic for image formats (see also "iff")
+#
+# 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.
+#
+# little magic: PCX (first byte is 0x0a)
+# no magic: Targa
+
+# PBMPLUS images
+# The next byte following the magic is always whitespace.
+0 string P1 PBM image text
+0 string P2 PGM image text
+0 string P3 PPM image text
+0 string P4 PBM "rawbits" image data
+0 string P5 PGM "rawbits" image data
+0 string P6 PPM "rawbits" image data
+
+# NIFF (Navy Interchange File Format, a modification of TIFF) images
+0 string IIN1 NIFF image data
+
+# Tag Image File Format, from Daniel Quinlan (quinlan@yggdrasil.com)
+# The second word of TIFF files is the TIFF version number, 42, which has
+# never changed. The TIFF specification recommends testing for it.
+0 string MM\x00\x2a TIFF image data, big-endian
+0 string II\x2a\x00 TIFF image data, little-endian
+
+# PNG [Portable Network Graphics, or "PNG's Not GIF"] images
+# (Greg Roelofs, newt@uchicago.edu)
+#
+# 137 P N G \r \n ^Z \n [4-byte length] H E A D [HEAD data] [HEAD crc] ...
+#
+0 string \x89PNG PNG image data,
+>4 belong !0x0d0a1a0a CORRUPTED,
+>16 belong x %ld x
+>20 belong x %ld,
+>24 byte x %d-bit
+>25 byte 0 grayscale,
+>25 byte 2 \b/color RGB,
+>25 byte 3 colormap,
+>25 byte 4 gray+alpha,
+>25 byte 6 \b/color RGBA,
+#>26 byte 0 deflate/32K,
+>28 byte 0 non-interlaced
+>28 byte 1 interlaced
+
+# GIF
+0 string GIF8 GIF image data
+>4 string 7a \b, version 8%s,
+>4 string 9a \b, version 8%s,
+>6 leshort >0 %hd x
+>8 leshort >0 %hd,
+#>10 byte &0x80 color mapped,
+#>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
+
+# ITC (CMU WM) raster files. It is essentially a byte-reversed Sun raster,
+# 1 plane, no encoding.
+0 string \361\0\100\273 CMU window manager raster image data
+>4 lelong >0 %d x
+>8 lelong >0 %d,
+>12 lelong >0 %d-bit
+
+# Magick Image File Format
+0 string id=ImageMagick MIFF image data
+
+# Artisan
+0 long 1123028772 Artisan image data
+>4 long 1 \b, rectangular 24-bit
+>4 long 2 \b, rectangular 8-bit with colormap
+>4 long 3 \b, rectangular 32-bit (24-bit with matte)
+
+# FIG (Facility for Interactive Generation of figures), an object-based format
+0 string #FIG FIG image text
+>5 string x \b, version %.3s
+
+# PHIGS
+0 string ARF_BEGARF PHIGS clear text archive
+0 string @(#)SunPHIGS SunPHIGS
+# version number follows, in the form m.n
+>40 string SunBin binary
+>32 string archive archive
+
+# GKS (Graphics Kernel System)
+0 string GKSM GKS Metafile
+>24 string SunGKS \b, SunGKS
+
+# CGM image files
+0 string BEGMF clear text Computer Graphics Metafile
+# XXX - questionable magic
+0 beshort&0xffe0 0x0020 binary Computer Graphics Metafile
+0 beshort 0x3020 character Computer Graphics Metafile
+
+# MGR bitmaps (Michael Haardt, u31b3hs@pool.informatik.rwth-aachen.de)
+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
+
+# Fuzzy Bitmap (FBM) images
+0 string %bitmap\0 FBM image data
+>30 long 0x31 \b, mono
+>30 long 0x33 \b, color
+
+# facsimile data
+1 string PC\ Research,\ Inc group 3 fax data
+>29 byte 0 \b, normal resolution (204x98 DPI)
+>29 byte 1 \b, fine resolution (204x196 DPI)
+
+# JPEG images
+# SunOS 5.5.1 had
+#
+# 0 string \377\330\377\340 JPEG file
+# 0 string \377\330\377\356 JPG file
+#
+# both of which turn into "JPEG image data" here.
+#
+0 beshort 0xffd8 JPEG image data
+>6 string JFIF \b, JFIF standard
+# HSI is Handmade Software's proprietary JPEG encoding scheme
+0 string hsi1 JPEG image data, HSI proprietary
+
+# PC bitmaps (OS/2, Windoze BMP files) (Greg Roelofs, newt@uchicago.edu)
+0 string BM PC bitmap data
+>14 leshort 12 \b, OS/2 1.x format
+>>18 leshort x \b, %d x
+>>20 leshort x %d
+>14 leshort 64 \b, OS/2 2.x format
+>>18 leshort x \b, %d x
+>>20 leshort x %d
+>14 leshort 40 \b, Windows 3.x format
+>>18 lelong x \b, %d x
+>>22 lelong x %d x
+>>28 leshort x %d
+0 string IC PC icon data
+0 string PI PC pointer image data
+0 string CI PC color icon data
+0 string CP PC color pointer image data
+# Conflicts with other entries [BABYL]
+#0 string BA PC bitmap array data
+
+# XPM icons (Greg Roelofs, newt@uchicago.edu)
+# note possible collision with C/REXX entry in c-lang; currently commented out
+0 string /*\ XPM\ */ X pixmap image text
+
+# Utah Raster Toolkit RLE images (janl@ifi.uio.no)
+0 leshort 0xcc52 RLE image data,
+>6 leshort x %d x
+>8 leshort x %d
+>2 leshort >0 \b, lower left corner: %d
+>4 leshort >0 \b, lower right corner: %d
+>10 byte&0x1 =0x1 \b, clear first
+>10 byte&0x2 =0x2 \b, no background
+>10 byte&0x4 =0x4 \b, alpha channel
+>10 byte&0x8 =0x8 \b, comment
+>11 byte >0 \b, %d color channels
+>12 byte >0 \b, %d bits per pixel
+>13 byte >0 \b, %d color map channels
+
+# image file format (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
+
+# Sun raster images, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 belong 0x59a66a95 Sun raster image data
+>4 belong >0 \b, %d x
+>8 belong >0 %d,
+>12 belong >0 %d-bit,
+#>16 belong >0 %d bytes long,
+>20 belong 0 old format,
+#>20 belong 1 standard,
+>20 belong 2 compressed,
+>20 belong 3 RGB,
+>20 belong 4 TIFF,
+>20 belong 5 IFF,
+>20 belong 0xffff reserved for testing,
+>24 belong 0 no colormap
+>24 belong 1 RGB colormap
+>24 belong 2 raw colormap
+#>28 belong >0 colormap is %d bytes long
+
+# SGI image file format, from Daniel Quinlan (quinlan@yggdrasil.com)
+# file://sgi.com/graphics/SGIIMAGESPEC
+0 beshort 474 SGI image data
+#>2 byte 0 \b, verbatim
+>2 byte 1 \b, RLE
+#>3 byte 1 \b, normal precision
+>3 byte 2 \b, high precision
+>4 beshort x \b, %d-D
+>6 beshort x \b, %d x
+>8 beshort x %d
+>10 beshort x \b, %d channel
+>10 beshort !1 \bs
+>80 string >0 \b, "%s"
+
+0 string IT01 FIT image data
+>4 belong x \b, %d x
+>8 belong x %d x
+>12 belong x %d
+#
+0 string IT02 FIT image data
+>4 belong x \b, %d x
+>8 belong x %d x
+>12 belong x %d
+#
+2048 string PCD_IPI Kodak Photo CD image pack file
+0 string PCD_OPA Kodak Photo CD overview pack file
+
+# FITS format. Jeff Uphoff <juphoff@tarsier.cv.nrao.edu>
+# FITS is the Flexible Image Transport System, the de facto standard for
+# data and image transfer, storage, etc., for the astronomical community.
+# (FITS floating point formats are big-endian.)
+0 string SIMPLE\ \ = FITS image data
+>109 string 8 \b, 8-bit, character or unsigned binary integer
+>108 string 16 \b, 16-bit, two's complement binary integer
+>107 string \ 32 \b, 32-bit, two's complement binary integer
+>107 string -32 \b, 32-bit, floating point, single precision
+>107 string -64 \b, 64-bit, floating point, double precision
+
+# other images
+0 string This\ is\ a\ BitMap\ file Lisp Machine bit-array-file
+0 string !! Bennet Yee's "face" format
+
+# From SunOS 5.5.1 "/etc/magic" - appeared right before Sun raster image
+# stuff.
+#
+0 beshort 0x1010 PEX Binary Archive
diff --git a/usr.bin/file/Magdir/intel b/usr.bin/file/Magdir/intel
new file mode 100644
index 0000000..d450e26
--- /dev/null
+++ b/usr.bin/file/Magdir/intel
@@ -0,0 +1,35 @@
+
+#------------------------------------------------------------------------------
+# intel: file(1) magic for x86 Unix
+#
+# Various flavors of x86 UNIX executable/object (other than Xenix, which
+# is in "microsoft"). DOS is in "msdos"; 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
+# SGI labeled the next entry as "iAPX 386 executable" --Dan Quinlan
+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..3eea3cf
--- /dev/null
+++ b/usr.bin/file/Magdir/interleaf
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# interleaf: file(1) magic for InterLeaf TPS:
+#
+0 string =\210OPS Interleaf saved data
+0 string =<!OPS Interleaf document text
+>5 string ,\ Version\ = \b, version
+>>17 string >\0 %.3s
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/island b/usr.bin/file/Magdir/island
new file mode 100644
index 0000000..9903cdd
--- /dev/null
+++ b/usr.bin/file/Magdir/island
@@ -0,0 +1,9 @@
+
+#------------------------------------------------------------------------------
+# island: file(1) magic for IslandWite/IslandDraw, from SunOS 5.5.1
+# "/etc/magic":
+# From: guy@netapp.com (Guy Harris)
+#
+4 string pgscriptver IslandWrite document
+13 string DrawFile IslandDraw document
+
diff --git a/usr.bin/file/Magdir/ispell b/usr.bin/file/Magdir/ispell
new file mode 100644
index 0000000..3c6bcdc
--- /dev/null
+++ b/usr.bin/file/Magdir/ispell
@@ -0,0 +1,54 @@
+
+#------------------------------------------------------------------------------
+# ispell: file(1) magic for ispell
+#
+# Ispell 3.0 has a magic of 0x9601 and ispell 3.1 has 0x9602. This magic
+# will match 0x9600 through 0x9603 in *both* little endian and big endian.
+# (No other current magic entries collide.)
+#
+# Updated by Daniel Quinlan (quinlan@yggdrasil.com)
+#
+0 leshort&0xFFFC 0x9600 little endian ispell
+>0 byte 0 hash file (?),
+>0 byte 1 3.0 hash file,
+>0 byte 2 3.1 hash file,
+>0 byte 3 hash file (?),
+>2 leshort 0x00 8-bit, no capitalization, 26 flags
+>2 leshort 0x01 7-bit, no capitalization, 26 flags
+>2 leshort 0x02 8-bit, capitalization, 26 flags
+>2 leshort 0x03 7-bit, capitalization, 26 flags
+>2 leshort 0x04 8-bit, no capitalization, 52 flags
+>2 leshort 0x05 7-bit, no capitalization, 52 flags
+>2 leshort 0x06 8-bit, capitalization, 52 flags
+>2 leshort 0x07 7-bit, capitalization, 52 flags
+>2 leshort 0x08 8-bit, no capitalization, 128 flags
+>2 leshort 0x09 7-bit, no capitalization, 128 flags
+>2 leshort 0x0A 8-bit, capitalization, 128 flags
+>2 leshort 0x0B 7-bit, capitalization, 128 flags
+>2 leshort 0x0C 8-bit, no capitalization, 256 flags
+>2 leshort 0x0D 7-bit, no capitalization, 256 flags
+>2 leshort 0x0E 8-bit, capitalization, 256 flags
+>2 leshort 0x0F 7-bit, capitalization, 256 flags
+>4 leshort >0 and %d string characters
+0 beshort&0xFFFC 0x9600 big endian ispell
+>1 byte 0 hash file (?),
+>1 byte 1 3.0 hash file,
+>1 byte 2 3.1 hash file,
+>1 byte 3 hash file (?),
+>2 beshort 0x00 8-bit, no capitalization, 26 flags
+>2 beshort 0x01 7-bit, no capitalization, 26 flags
+>2 beshort 0x02 8-bit, capitalization, 26 flags
+>2 beshort 0x03 7-bit, capitalization, 26 flags
+>2 beshort 0x04 8-bit, no capitalization, 52 flags
+>2 beshort 0x05 7-bit, no capitalization, 52 flags
+>2 beshort 0x06 8-bit, capitalization, 52 flags
+>2 beshort 0x07 7-bit, capitalization, 52 flags
+>2 beshort 0x08 8-bit, no capitalization, 128 flags
+>2 beshort 0x09 7-bit, no capitalization, 128 flags
+>2 beshort 0x0A 8-bit, capitalization, 128 flags
+>2 beshort 0x0B 7-bit, capitalization, 128 flags
+>2 beshort 0x0C 8-bit, no capitalization, 256 flags
+>2 beshort 0x0D 7-bit, no capitalization, 256 flags
+>2 beshort 0x0E 8-bit, capitalization, 256 flags
+>2 beshort 0x0F 7-bit, capitalization, 256 flags
+>4 beshort >0 and %d string characters
diff --git a/usr.bin/file/Magdir/java b/usr.bin/file/Magdir/java
new file mode 100644
index 0000000..219f93e
--- /dev/null
+++ b/usr.bin/file/Magdir/java
@@ -0,0 +1,13 @@
+#------------------------------------------------------------
+# Java ByteCode
+# From Larry Schwimmer (schwim@cs.stanford.edu)
+0 belong 0xcafebabe
+>4 belong 0x0003002d Java bytecode
+#
+# java: file(1) magic for java compiled classes
+#
+
+0 belong 0xCafeBabe compiled java class data,
+>4 beshort x version %d.
+>6 beshort x \b%d
+
diff --git a/usr.bin/file/Magdir/karma b/usr.bin/file/Magdir/karma
new file mode 100644
index 0000000..e256abf
--- /dev/null
+++ b/usr.bin/file/Magdir/karma
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# karma: file(1) magic for Karma data files
+#
+# From <rgooch@atnf.csiro.au>
+
+0 string KarmaRHD Version Karma Data Structure Version
+>16 long x %lu
diff --git a/usr.bin/file/Magdir/lex b/usr.bin/file/Magdir/lex
new file mode 100644
index 0000000..7b6d0f7
--- /dev/null
+++ b/usr.bin/file/Magdir/lex
@@ -0,0 +1,11 @@
+
+#------------------------------------------------------------------------------
+# lex: file(1) magic for lex
+#
+# derived empirically, your offsets may vary!
+53 string yyprevious C program text (from lex)
+>3 string >\0 for %s
+# C program text from GNU flex, from Daniel Quinlan <quinlan@yggdrasil.com>
+21 string generated\ by\ flex C program text (from flex)
+# lex description file, from Daniel Quinlan <quinlan@yggdrasil.com>
+0 string %{ lex description text
diff --git a/usr.bin/file/Magdir/lif b/usr.bin/file/Magdir/lif
new file mode 100644
index 0000000..f6d7901
--- /dev/null
+++ b/usr.bin/file/Magdir/lif
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# lif: file(1) magic for lif
+#
+# XXX - byte order? (Probably beshort, Daniel Quinlan <quinlan@yggdrasil.com>)
+#
+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..75a2a2b
--- /dev/null
+++ b/usr.bin/file/Magdir/linux
@@ -0,0 +1,73 @@
+
+#------------------------------------------------------------------------------
+# linux: file(1) magic for Linux files
+#
+# Values for Linux/i386 binaries, from Daniel Quinlan <quinlan@yggdrasil.com>
+# The following basic Linux magic is useful for reference, but using
+# "long" magic is a better practice in order to avoid collisions.
+#
+# 2 leshort 100 Linux/i386
+# >0 leshort 0407 impure executable (OMAGIC)
+# >0 leshort 0410 pure executable (NMAGIC)
+# >0 leshort 0413 demand-paged executable (ZMAGIC)
+# >0 leshort 0314 demand-paged executable (QMAGIC)
+#
+0 lelong 0x00640107 Linux/i386 impure executable (OMAGIC)
+>16 lelong 0 \b, stripped
+0 lelong 0x00640108 Linux/i386 pure executable (NMAGIC)
+>16 lelong 0 \b, stripped
+0 lelong 0x0064010b Linux/i386 demand-paged executable (ZMAGIC)
+>16 lelong 0 \b, stripped
+0 lelong 0x006400cc Linux/i386 demand-paged executable (QMAGIC)
+>16 lelong 0 \b, stripped
+#
+0 string \007\001\000 Linux/i386 object file
+>20 lelong >0x1020 \b, DLL library
+# message catalogs, from Mitchum DSouza <m.dsouza@mrc-apu.cam.ac.uk>
+0 string *nazgul* Linux compiled message catalog
+>8 lelong >0 \b, version %ld
+# core dump file, from Bill Reynolds <bill@goshawk.lanl.gov>
+216 lelong 0421 Linux/i386 core file
+>220 string >\0 of '%s'
+>200 lelong >0 (signal %d)
+#
+# LILO boot/chain loaders, from Daniel Quinlan <quinlan@yggdrasil.com>
+# this can be overridden by the DOS executable (COM) entry
+2 string LILO Linux/i386 LILO boot/chain loader
+#
+# Debian Packages, from Peter Tobias <tobias@server.et-inf.fho-emden.de>
+0 string 0.9
+>8 byte 0x0a Debian Binary Package
+>>3 byte >0 \b, created by dpkg 0.9%c
+>>4 byte >0 pl%c
+# PSF fonts, from H. Peter Anvin <hpa@yggdrasil.com>
+0 leshort 0x0436 Linux/i386 PC Screen Font data,
+>2 byte 0 256 characters, no directory,
+>2 byte 1 512 characters, no directory,
+>2 byte 2 256 characters, Unicode directory,
+>2 byte 3 512 characters, Unicode directory,
+>3 byte >0 8x%d
+# Linux swap file, from Daniel Quinlan <quinlan@yggdrasil.com>
+4086 string SWAP-SPACE Linux/i386 swap file
+# ECOFF magic for OSF/1 and Linux (only tested under Linux though)
+#
+# from Erik Troan (ewt@redhat.com) examining od dumps, so this
+# could be wrong
+# updated by David Mosberger (davidm@azstarnet.com) based on
+# GNU BFD and MIPS info found below.
+#
+0 leshort 0x0183 ECOFF alpha
+>24 leshort 0407 executable
+>24 leshort 0410 pure
+>24 leshort 0413 demand paged
+>8 long >0 not stripped
+>8 long 0 stripped
+>23 leshort >0 - version %ld.
+# linux Kernel images version 1.3.80 - ?
+# from Axel Kohlmeyer <akohlmey@rincewind.chemie.uni-ulm.de>
+0 belong 0xb8c0078e Linux/x86 kernel image,
+>0x048c byte 0x31
+>>0x048c string x version %s
+>0x0493 byte 0x31
+>>0x0493 string x version %s
+#
diff --git a/usr.bin/file/Magdir/lisp b/usr.bin/file/Magdir/lisp
new file mode 100644
index 0000000..ac4ba77
--- /dev/null
+++ b/usr.bin/file/Magdir/lisp
@@ -0,0 +1,10 @@
+
+#------------------------------------------------------------------------------
+# lisp: file(1) magic for lisp programs
+#
+# various lisp types, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 string ;; Lisp/Scheme program text
+# Emacs 18 - this is always correct, but not very magical.
+0 string \012( byte-compiled Emacs-Lisp program data
+# Emacs 19
+0 string ;ELC\023\000\000\000 byte-compiled Emacs-Lisp program data
diff --git a/usr.bin/file/Magdir/mach b/usr.bin/file/Magdir/mach
new file mode 100644
index 0000000..308325e
--- /dev/null
+++ b/usr.bin/file/Magdir/mach
@@ -0,0 +1,38 @@
+#------------------------------------------------------------------------------
+# mach file description
+#
+0 belong 0xcafebabe mach-o fat file
+>4 belong 1 with 1 architecture
+>4 belong >1
+>>4 belong x with %ld architectures
+#
+0 belong 0xfeedface mach-o
+>12 belong 1 object
+>12 belong 2 executable
+>12 belong 3 shared library
+>12 belong 4 core
+>12 belong 5 preload executable
+>12 belong >5
+>>12 belong x filetype=%ld
+>4 belong <0
+>>4 belong x architecture=%ld
+>4 belong 1 vax
+>4 belong 2 romp
+>4 belong 3 architecture=3
+>4 belong 4 ns32032
+>4 belong 5 ns32332
+>4 belong 6 for m68k architecture
+>4 belong 7 i386
+>4 belong 8 mips
+>4 belong 9 ns32532
+>4 belong 10 architecture=10
+>4 belong 11 hp pa-risc
+>4 belong 12 acorn
+>4 belong 13 m88k
+>4 belong 14 sparc
+>4 belong 15 i860-big
+>4 belong 16 i860
+>4 belong 17 rs6000
+>4 belong 18 powerPC
+>4 belong >18
+>>4 belong x architecture=%ld
diff --git a/usr.bin/file/Magdir/magic b/usr.bin/file/Magdir/magic
new file mode 100644
index 0000000..4a639c6
--- /dev/null
+++ b/usr.bin/file/Magdir/magic
@@ -0,0 +1,5 @@
+
+#------------------------------------------------------------------------------
+# magic: file(1) magic for magic files
+#
+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..bd3fd2d
--- /dev/null
+++ b/usr.bin/file/Magdir/mail.news
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# mail.news: file(1) magic for mail and news
+#
+# 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
+0 string BABYL Emacs RMAIL text
+0 string Received: RFC 822 mail text
+0 string MIME-Version: MIME entity text
+0 string Content- MIME entity text
diff --git a/usr.bin/file/Magdir/microsoft b/usr.bin/file/Magdir/microsoft
new file mode 100644
index 0000000..74d1daf
--- /dev/null
+++ b/usr.bin/file/Magdir/microsoft
@@ -0,0 +1,72 @@
+
+#------------------------------------------------------------------------------
+# microsoft: file(1) magic for Microsoft Xenix
+#
+# "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
+>0x1e leshort &0x4000 V2.3
+>0x1e leshort &0x8000 V3.0
+>0x1c byte &0x4 86
+>0x1c byte &0xb 186
+>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..73c3747
--- /dev/null
+++ b/usr.bin/file/Magdir/mirage
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# mirage: file(1) magic for Mirage executables
+#
+# 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..dfb2d93
--- /dev/null
+++ b/usr.bin/file/Magdir/mkid
@@ -0,0 +1,10 @@
+
+#------------------------------------------------------------------------------
+# mkid: file(1) magic for mkid(1) databases
+#
+# 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..72cd9f3
--- /dev/null
+++ b/usr.bin/file/Magdir/mmdf
@@ -0,0 +1,5 @@
+
+#------------------------------------------------------------------------------
+# mmdf: file(1) magic for MMDF mail files
+#
+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..efed159
--- /dev/null
+++ b/usr.bin/file/Magdir/motorola
@@ -0,0 +1,32 @@
+
+#------------------------------------------------------------------------------
+# motorola: file(1) magic for Motorola 68K and 88K binaries
+#
+# 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
+#
+# Motorola S-Records, from Gerd Truschinski <gt@freebsd.first.gmd.de>
+0 string S0 Motorola S-Record; binary data in text format
diff --git a/usr.bin/file/Magdir/ms-dos b/usr.bin/file/Magdir/ms-dos
new file mode 100644
index 0000000..db2c03e
--- /dev/null
+++ b/usr.bin/file/Magdir/ms-dos
@@ -0,0 +1,73 @@
+
+#------------------------------------------------------------------------------
+# msdos: file(1) magic for MS-DOS files
+#
+
+# .BAT files (Daniel Quinlan, quinlan@yggdrasil.com)
+0 string @echo\ off MS-DOS batch file text
+
+# .EXE formats (Greg Roelofs, newt@uchicago.edu)
+#
+0 string MZ MS-DOS executable (EXE)
+>24 string @ \b, OS/2 or Windows
+>1638 string -lh5- \b, LHa SFX archive v2.13S
+>7195 string Rar! \b, RAR self-extracting archive
+#
+# [GRR 950118: file 3.15 has a buffer-size limitation; offsets bigger than
+# 8161 bytes are ignored. To make the following entries work, increase
+# HOWMANY in file.h to 32K at least, and maybe to 70K or more for OS/2,
+# NT/Win32 and VMS.]
+# [GRR: some company sells a self-extractor/displayer for image data(!)]
+#
+>11696 string PK\003\004 \b, PKZIP SFX archive v1.1
+>13297 string PK\003\004 \b, PKZIP SFX archive v1.93a
+>15588 string PK\003\004 \b, PKZIP2 SFX archive v1.09
+>15770 string PK\003\004 \b, PKZIP SFX archive v2.04g
+>28374 string PK\003\004 \b, PKZIP2 SFX archive v1.02
+#
+# Info-ZIP self-extractors
+# these are the DOS versions:
+>25115 string PK\003\004 \b, Info-ZIP SFX archive v5.12
+>26331 string PK\003\004 \b, Info-ZIP SFX archive v5.12 w/decryption
+# these are the OS/2 versions (OS/2 is flagged above):
+>47031 string PK\003\004 \b, Info-ZIP SFX archive v5.12
+>49845 string PK\003\004 \b, Info-ZIP SFX archive v5.12 w/decryption
+# this is the NT/Win32 version:
+>69120 string PK\003\004 \b, Info-ZIP NT SFX archive v5.12 w/decryption
+#
+# TELVOX Teleinformatica CODEC self-extractor for OS/2:
+>49801 string \x79\xff\x80\xff\x76\xff \b, CODEC archive v3.21
+>>49824 leshort =1 \b, 1 file
+>>49824 leshort >1 \b, %u files
+
+# .COM formats (Daniel Quinlan, quinlan@yggdrasil.com)
+# Uncommenting only the first two lines will cover about 2/3 of COM files,
+# but it isn't feasible to match all COM files since there must be at least
+# two dozen different one-byte "magics".
+#0 byte 0xe9 MS-DOS executable (COM)
+#0 byte 0x8c MS-DOS executable (COM)
+# 0xeb conflicts with "sequent" magic
+#0 byte 0xeb MS-DOS executable (COM)
+#0 byte 0xb8 MS-DOS executable (COM)
+
+# miscellaneous formats
+0 string LZ MS-DOS executable (built-in)
+#0 byte 0xf0 MS-DOS program library data
+#
+
+# Popular applications
+2080 string Microsoft\ Word\ 6.0\ Document %s
+#
+0 belong 0x31be0000 Microsoft Word Document
+#
+2080 string Microsoft\ Excel\ 5.0\ Worksheet %s
+#
+0 belong 0x00001a00 Lotus 1-2-3
+>4 belong 0x00100400 wk3 document
+>4 belong 0x02100400 wk4 document
+>4 belong 0x07800100 fm3 or fmb document
+>4 belong 0x07800000 fm3 or fmb document
+#
+0 belong 0x00000200 Lotus 1-2-3
+>4 belong 0x06040600 wk1 document
+>4 belong 0x06800200 fmt document
diff --git a/usr.bin/file/Magdir/ncr b/usr.bin/file/Magdir/ncr
new file mode 100644
index 0000000..987c94e
--- /dev/null
+++ b/usr.bin/file/Magdir/ncr
@@ -0,0 +1,48 @@
+
+#------------------------------------------------------------------------------
+# ncr: file(1) magic for 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..7d92ef5
--- /dev/null
+++ b/usr.bin/file/Magdir/netbsd
@@ -0,0 +1,209 @@
+
+#------------------------------------------------------------------------------
+# netbsd: file(1) magic for NetBSD objects
+#
+# All new-style magic numbers are in network byte order.
+#
+
+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
+>>0 byte &0x40 position independent
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+0 belong&0377777777 041400507 NetBSD/i386 core
+>12 string >\0 from '%s'
+
+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
+>>0 byte &0x40 position independent
+>>20 belong !0 executable
+>>20 belong =0 object file
+>16 belong >0 not stripped
+0 belong&0377777777 041600507 NetBSD/m68k core
+>12 string >\0 from '%s'
+
+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
+>>0 byte &0x40 position independent
+>>20 belong !0 executable
+>>20 belong =0 object file
+>16 belong >0 not stripped
+0 belong&0377777777 042000507 NetBSD/m68k4k core
+>12 string >\0 from '%s'
+
+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
+>>0 byte &0x40 position independent
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+0 belong&0377777777 042200507 NetBSD/ns32532 core
+>12 string >\0 from '%s'
+
+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
+>>0 byte &0x40 position independent
+>>20 belong !0 executable
+>>20 belong =0 object file
+>16 belong >0 not stripped
+0 belong&0377777777 042400507 NetBSD/sparc core
+>12 string >\0 from '%s'
+
+0 belong&0377777777 042600413 NetBSD/pmax 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 042600410 NetBSD/pmax pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 042600407 NetBSD/pmax
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+0 belong&0377777777 042600507 NetBSD/pmax core
+>12 string >\0 from '%s'
+
+0 belong&0377777777 043000413 NetBSD/vax 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 043000410 NetBSD/vax pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 043000407 NetBSD/vax
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+0 belong&0377777777 043000507 NetBSD/vax core
+>12 string >\0 from '%s'
+
+# NetBSD/alpha does not support (and has never supported) a.out objects,
+# so no rules are provided for them. NetBSD/alpha ELF objects are
+# dealt with in "elf".
+0 leshort 0x00070185 ECOFF NetBSD/alpha binary
+>10 leshort 0x0001 not stripped
+>10 leshort 0x0000 stripped
+0 belong&0377777777 043200507 NetBSD/alpha core
+>12 string >\0 from '%s'
+
+0 belong&0377777777 043400413 NetBSD/mips 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 043400410 NetBSD/mips pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 043400407 NetBSD/mips
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 belong !0 executable
+>>20 belong =0 object file
+>16 belong >0 not stripped
+0 belong&0377777777 043400507 NetBSD/mips core
+>12 string >\0 from '%s'
+
+0 belong&0377777777 043600413 NetBSD/arm32 demand paged
+>0 byte &0x80
+>>20 lelong <8192 shared library
+>>20 lelong =8192 dynamically linked executable
+>>20 lelong >8192 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 043600410 NetBSD/arm32 pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 043600407 NetBSD/arm32
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+0 belong&0377777777 043600507 NetBSD/arm32 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..0ac4fa2
--- /dev/null
+++ b/usr.bin/file/Magdir/news
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# news: file(1) magic for SunOS NeWS fonts (not "news" as in "netnews")
+#
+0 string StartFontMetrics ASCII font metrics
+0 string StartFont ASCII font bits
+0 belong 0x137A2944 NeWS bitmap font
+0 belong 0x137A2947 NeWS font family
+0 belong 0x137A2950 scalable OpenFont binary
+0 belong 0x137A2951 encrypted scalable OpenFont binary
+8 belong 0x137A2B45 X11/NeWS bitmap font
+8 belong 0x137A2B48 X11/NeWS font family
diff --git a/usr.bin/file/Magdir/osf1 b/usr.bin/file/Magdir/osf1
new file mode 100644
index 0000000..d2868c3
--- /dev/null
+++ b/usr.bin/file/Magdir/osf1
@@ -0,0 +1,10 @@
+#
+# Mach magic number info
+#
+0 long 0xefbe OSF/Rose object
+# I386 magic number info
+#
+0 short 0565 i386 COFF object
+#
+0 string Core Alpha Digital UNIX core file
+>24 string >\0 \b, generated from '%s'
diff --git a/usr.bin/file/Magdir/pbm b/usr.bin/file/Magdir/pbm
new file mode 100644
index 0000000..98c15f7
--- /dev/null
+++ b/usr.bin/file/Magdir/pbm
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# pbm: file(1) magic for Portable Bitmap files
+#
+# XXX - byte order?
+#
+0 short 0x2a17 "compact bitmap" format (Poskanzer)
diff --git a/usr.bin/file/Magdir/pdf b/usr.bin/file/Magdir/pdf
new file mode 100644
index 0000000..a1aef13
--- /dev/null
+++ b/usr.bin/file/Magdir/pdf
@@ -0,0 +1,7 @@
+#------------------------------------------------------------------------------
+# pdf: file(1) magic for Portable Document Format
+#
+
+0 string %PDF- PDF document
+>5 byte x \b, version %c
+>7 byte x \b.%c
diff --git a/usr.bin/file/Magdir/pdp b/usr.bin/file/Magdir/pdp
new file mode 100644
index 0000000..201dede
--- /dev/null
+++ b/usr.bin/file/Magdir/pdp
@@ -0,0 +1,25 @@
+
+#------------------------------------------------------------------------------
+# pdp: file(1) magic for 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
+>15 byte >0 - version %ld
+
+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
+>15 byte >0 - version %ld
+
+0 leshort 0411 PDP-11 separate I&D executable
+>8 leshort >0 not stripped
+>15 byte >0 - version %ld
+
+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..038d098
--- /dev/null
+++ b/usr.bin/file/Magdir/pgp
@@ -0,0 +1,13 @@
+
+#------------------------------------------------------------------------------
+# pgp: file(1) magic for 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 beshort 0xa600 PGP encrypted data
+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..dc8ef5d
--- /dev/null
+++ b/usr.bin/file/Magdir/pkgadd
@@ -0,0 +1,5 @@
+
+#------------------------------------------------------------------------------
+# pkgadd: file(1) magic for 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..acf3bf4
--- /dev/null
+++ b/usr.bin/file/Magdir/plus5
@@ -0,0 +1,17 @@
+
+#------------------------------------------------------------------------------
+# plus5: file(1) magic 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/printer b/usr.bin/file/Magdir/printer
new file mode 100644
index 0000000..d20330f
--- /dev/null
+++ b/usr.bin/file/Magdir/printer
@@ -0,0 +1,55 @@
+
+#------------------------------------------------------------------------------
+# printer: file(1) magic for printer-formatted files
+#
+
+# PostScript, updated by Daniel Quinlan (quinlan@yggdrasil.com)
+0 string %! PostScript document text
+>2 string PS-Adobe- conforming
+>>11 string >\0 at level %.3s
+>>>15 string EPS - type %s
+>>>15 string Query - type %s
+>>>15 string ExitServer - type %s
+# Some PCs have the annoying habit of adding a ^D as a document separator
+0 string \004%! PostScript document text
+>3 string PS-Adobe- conforming
+>>12 string >\0 at level %.3s
+>>>16 string EPS - type %s
+>>>16 string Query - type %s
+>>>16 string ExitServer - type %s
+
+# HP Printer Job Language
+0 string \033%-12345X@PJL HP Printer Job Language data
+>15 string \ ENTER\ LANGUAGE\ =
+>31 string PostScript PostScript
+
+# HP Printer Control Language, Daniel Quinlan (quinlan@yggdrasil.com)
+0 string \033E\033 HP PCL printer data
+>3 string \&l0A - default page size
+>3 string \&l1A - US executive page size
+>3 string \&l2A - US letter page size
+>3 string \&l3A - US legal page size
+>3 string \&l26A - A4 page size
+>3 string \&l80A - Monarch envelope size
+>3 string \&l81A - No. 10 envelope size
+>3 string \&l90A - Intl. DL envelope size
+>3 string \&l91A - Intl. C5 envelope size
+>3 string \&l100A - Intl. B5 envelope size
+>3 string \&l-81A - No. 10 envelope size (landscape)
+>3 string \&l-90A - Intl. DL envelope size (landscape)
+
+# 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.
+# [GRR 950115: missing "postscript" or "ultrascript" (whatever it was called)]
+#
+# 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/psdbms b/usr.bin/file/Magdir/psdbms
new file mode 100644
index 0000000..f36121f
--- /dev/null
+++ b/usr.bin/file/Magdir/psdbms
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# psdbms: file(1) magic for psdatabase
+#
+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..fe16608
--- /dev/null
+++ b/usr.bin/file/Magdir/pyramid
@@ -0,0 +1,11 @@
+
+#------------------------------------------------------------------------------
+# pyramid: file(1) 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/rpm b/usr.bin/file/Magdir/rpm
new file mode 100644
index 0000000..14ad6db
--- /dev/null
+++ b/usr.bin/file/Magdir/rpm
@@ -0,0 +1,17 @@
+#------------------------------------------------------------------------------
+#
+# RPM: file(1) magic for Red Hat Packages Erik Troan (ewt@redhat.com)
+#
+0 beshort 0xedab
+>2 beshort 0xeedb RPM
+>>4 byte x v%d
+>>6 beshort 0 bin
+>>6 beshort 1 src
+>>8 beshort 1 i386
+>>8 beshort 2 Alpha
+>>8 beshort 3 Sparc
+>>8 beshort 4 MIPS
+>>8 beshort 5 PowerPC
+>>8 beshort 6 68000
+>>8 beshort 7 SGI
+>>10 string x %s
diff --git a/usr.bin/file/Magdir/rtf b/usr.bin/file/Magdir/rtf
new file mode 100644
index 0000000..8e2d416
--- /dev/null
+++ b/usr.bin/file/Magdir/rtf
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# rtf: file(1) magic for Rich Text Format (RTF)
+#
+# Duncan P. Simpson, D.P.Simpson@dcs.warwick.ac.uk
+#
+0 string {\\rtf Rich Text Format data,
+>5 byte x version %c,
+>6 string \\ansi ANSI
+>6 string \\mac Apple Macintosh
+>6 string \\pc IBM PC, code page 437
+>6 string \\pca IBM PS/2, code page 850
diff --git a/usr.bin/file/Magdir/sc b/usr.bin/file/Magdir/sc
new file mode 100644
index 0000000..98599f2
--- /dev/null
+++ b/usr.bin/file/Magdir/sc
@@ -0,0 +1,5 @@
+
+#------------------------------------------------------------------------------
+# sc: file(1) magic for "sc" spreadsheet
+#
+38 string Spreadsheet sc spreadsheet file
diff --git a/usr.bin/file/Magdir/sccs b/usr.bin/file/Magdir/sccs
new file mode 100644
index 0000000..11d50b2
--- /dev/null
+++ b/usr.bin/file/Magdir/sccs
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# sccs: file(1) magic for SCCS archives
+#
+# 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 data
diff --git a/usr.bin/file/Magdir/sendmail b/usr.bin/file/Magdir/sendmail
new file mode 100644
index 0000000..503ef89
--- /dev/null
+++ b/usr.bin/file/Magdir/sendmail
@@ -0,0 +1,10 @@
+
+#------------------------------------------------------------------------------
+# sendmail: file(1) magic for sendmail config files
+#
+# 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..e6f7b52
--- /dev/null
+++ b/usr.bin/file/Magdir/sequent
@@ -0,0 +1,34 @@
+
+#------------------------------------------------------------------------------
+# sequent: file(1) magic for Sequent machines
+#
+# 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/sgi b/usr.bin/file/Magdir/sgi
new file mode 100644
index 0000000..ce9dbc8
--- /dev/null
+++ b/usr.bin/file/Magdir/sgi
@@ -0,0 +1,170 @@
+
+#------------------------------------------------------------------------------
+# sgi: file(1) magic for Silicon Graphics (MIPS, IRIS, IRIX, etc.)
+# Dec Ultrix (MIPS)
+# all of SGI's *current* machines and OSes run in big-endian mode on the
+# MIPS machines, as far as I know.
+#
+# XXX - what is the blank "-" line?
+#
+# kbd file definitions
+0 string kbd!map kbd map file
+>8 byte >0 Ver %d:
+>10 short >0 with %d table(s)
+0 belong 0407 old SGI 68020 executable
+0 belong 0410 old SGI 68020 pure executable
+0 beshort 0x8765 disk quotas file
+0 beshort 0x0506 IRIS Showcase file
+>2 byte 0x49 -
+>3 byte x - version %ld
+0 beshort 0x0226 IRIS Showcase template
+>2 byte 0x63 -
+>3 byte x - version %ld
+0 belong 0x5343464d IRIS Showcase file
+>4 byte x - version %ld
+0 belong 0x5443464d IRIS Showcase template
+>4 byte x - version %ld
+0 belong 0xdeadbabe IRIX Parallel Arena
+>8 belong >0 - version %ld
+#
+0 beshort 0x0160 MIPSEB COFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %ld
+>23 byte x .%ld
+#
+0 beshort 0x0162 MIPSEL COFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x .%ld
+#
+0 beshort 0x6001 MIPSEB-LE COFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x .%ld
+#
+0 beshort 0x6201 MIPSEL-LE COFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %ld
+>22 byte x .%ld
+#
+# MIPS 2 additions
+#
+0 beshort 0x0163 MIPSEB MIPS-II COFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %ld
+>23 byte x .%ld
+#
+0 beshort 0x0166 MIPSEL MIPS-II COFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %ld
+>23 byte x .%ld
+#
+0 beshort 0x6301 MIPSEB-LE MIPS-II COFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %ld
+>22 byte x .%ld
+#
+0 beshort 0x6601 MIPSEL-LE MIPS-II COFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %ld
+>22 byte x .%ld
+#
+# MIPS 3 additions
+#
+0 beshort 0x0140 MIPSEB MIPS-III COFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %ld
+>23 byte x .%ld
+#
+0 beshort 0x0142 MIPSEL MIPS-III COFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %ld
+>23 byte x .%ld
+#
+0 beshort 0x4001 MIPSEB-LE MIPS-III COFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %ld
+>22 byte x .%ld
+#
+0 beshort 0x4201 MIPSEL-LE MIPS-III COFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %ld
+>22 byte x .%ld
+#
+0 beshort 0x180 MIPSEB Ucode
+0 beshort 0x182 MIPSEL Ucode
+# 32bit core file
+0 belong 0xdeadadb0 IRIX core dump
+>4 belong 1 of
+>16 string >\0 '%s'
+# 64bit core file
+0 belong 0xdeadad40 IRIX 64-bit core dump
+>4 belong 1 of
+>16 string >\0 '%s'
+# New style crash dump file
+0 string \x43\x72\x73\x68\x44\x75\x6d\x70 IRIX vmcore dump of
+>36 string >\0 '%s'
+# Trusted IRIX info
+0 string SGIAUDIT SGI Audit file
+>8 byte x - version %d
+>9 byte x .%ld
+# Are these three SGI-based file types or general ones?
+0 string WNGZWZSC Wingz compiled script
+0 string WNGZWZSS Wingz spreadsheet
+0 string WNGZWZHP Wingz help file
+#
+0 string \#Inventor V IRIS Inventor 1.0 file
+0 string \#Inventor V2 Open Inventor 2.0 file
+# XXX - I don't know what next thing is! It is likely to be an image
+# (or movie) format
+0 string glfHeadMagic(); GLF_TEXT
+4 belong 0x41010000 GLF_BINARY_LSB_FIRST
+4 belong 0x00000141 GLF_BINARY_MSB_FIRST
diff --git a/usr.bin/file/Magdir/sgml b/usr.bin/file/Magdir/sgml
new file mode 100644
index 0000000..985bbec
--- /dev/null
+++ b/usr.bin/file/Magdir/sgml
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# sgml: file(1) magic for Standard Generalized Markup Language
+
+# HyperText Markup Language (HTML) is an SGML document type,
+# from Daniel Quinlan (quinlan@yggdrasil.com)
+0 string \<!DOCTYPE\ HTML HTML document text
+0 string \<!doctype\ html HTML document text
+0 string \<HEAD HTML document text
+0 string \<head HTML document text
+0 string \<TITLE HTML document text
+0 string \<title HTML document text
+0 string \<html HTML document text
+0 string \<HTML HTML document text
+
+# SGML, mostly from rph@sq
+0 string \<!DOCTYPE exported SGML document text
+0 string \<!doctype exported SGML document text
+0 string \<!SUBDOC exported SGML subdocument text
+0 string \<!subdoc exported SGML subdocument text
+0 string \<!-- exported SGML document text
diff --git a/usr.bin/file/Magdir/sniffer b/usr.bin/file/Magdir/sniffer
new file mode 100644
index 0000000..861ec8c
--- /dev/null
+++ b/usr.bin/file/Magdir/sniffer
@@ -0,0 +1,63 @@
+
+#------------------------------------------------------------------------------
+# sniffer: file(1) magic for packet captured files
+#
+# From: guy@netapp.com (Guy Harris)
+#
+# Microsoft NetMon (packet capture/display program) capture files.
+#
+0 string RTSS NetMon capture file
+>4 byte x - version %d
+>5 byte x \b.%d
+#
+# Network General Sniffer capture files (the Sniffer software does,
+# after all, run under MS-DOS...).
+#
+0 string TRSNIFF\ data\ \ \ \ \032 Sniffer capture file
+>23 leshort x - version %d
+>25 leshort x \b.%d
+>33 byte x (Format %d,
+>32 byte 0 Token ring)
+>32 byte 1 Ethernet)
+>32 byte 2 ARCnet)
+>32 byte 3 StarLAN)
+>32 byte 4 PC Network broadband)
+>32 byte 5 LocalTalk)
+>32 byte 6 Znet)
+#
+# (We call them "tcpdump capture file(s)" for now, as "tcpdump" is
+# the main program that uses that format, but there's also "tcpview",
+# and there may be others in the future.)
+#
+0 ubelong 0xa1b2c3d4 tcpdump capture file (big-endian)
+>4 beshort x - version %d
+>6 beshort x \b.%d
+>20 belong 0 (No link-layer encapsulation
+>20 belong 1 (Ethernet
+>20 belong 2 (3Mb Ethernet
+>20 belong 3 (AX.25
+>20 belong 4 (ProNet
+>20 belong 5 (Chaos
+>20 belong 6 (IEEE 802.x network
+>20 belong 7 (ARCnet
+>20 belong 8 (SLIP
+>20 belong 9 (PPP
+>20 belong 10 (FDDI
+>20 belong 11 (RFC 1483 ATM
+>16 belong x \b, capture length %d)
+0 ulelong 0xa1b2c3d4 tcpdump capture file (little-endian)
+>4 leshort x - version %d
+>6 leshort x \b.%d
+>20 lelong 0 (No link-layer encapsulation
+>20 lelong 1 (Ethernet
+>20 lelong 2 (3Mb Ethernet
+>20 lelong 3 (AX.25
+>20 lelong 4 (ProNet
+>20 lelong 5 (Chaos
+>20 lelong 6 (IEEE 802.x network
+>20 lelong 7 (ARCnet
+>20 lelong 8 (SLIP
+>20 lelong 9 (PPP
+>20 lelong 10 (FDDI
+>20 lelong 11 (RFC 1483 ATM
+>16 lelong x \b, capture length %d)
diff --git a/usr.bin/file/Magdir/softquad b/usr.bin/file/Magdir/softquad
new file mode 100644
index 0000000..f570b09
--- /dev/null
+++ b/usr.bin/file/Magdir/softquad
@@ -0,0 +1,30 @@
+
+#------------------------------------------------------------------------------
+# softquad: file(1) magic for SoftQuad Publishing Software
+#
+# $Id$
+# 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..2f0336a
--- /dev/null
+++ b/usr.bin/file/Magdir/sun
@@ -0,0 +1,110 @@
+
+#------------------------------------------------------------------------------
+# sun: file(1) magic for Sun machines
+#
+# 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'
+# Sun SunPC
+0 long 0xfa33c08e SunPC 4.0 Hard Disk
+0 string #SUNPC_CONFIG SunPC 4.0 Properties Values
+# Sun snoop
+#
+# XXX - are numbers stored in big-endian format, or in host byte order?
+# They're the same on SPARC, but not the same on x86.
+#
+0 string snoop Snoop capture file
+>8 long >0 - version %ld
+>12 long 0 (IEEE 802.3)
+>12 long 1 (IEEE 802.4)
+>12 long 2 (IEEE 802.5)
+>12 long 3 (IEEE 802.6)
+>12 long 4 (Ethernet)
+>12 long 5 (HDLC)
+>12 long 6 (Character synchronous)
+>12 long 7 (IBM channel-to-channel adapter)
+>12 long 8 (FDDI)
+>12 long 9 (Unknown)
+# Sun KCMS
+36 string acsp Kodak Color Management System, ICC Profile
+
+
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..2226ce8
--- /dev/null
+++ b/usr.bin/file/Magdir/terminfo
@@ -0,0 +1,9 @@
+
+#------------------------------------------------------------------------------
+# terminfo: file(1) magic for 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..5126be8
--- /dev/null
+++ b/usr.bin/file/Magdir/tex
@@ -0,0 +1,36 @@
+
+#------------------------------------------------------------------------------
+# tex: file(1) magic for TeX files
+#
+# 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
+>3 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
+>33 string >\0 (%s)
+2 string \000\022 TeX font metric data
+>33 string >\0 (%s)
+
+# Texinfo and GNU Info, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 string \\input\ texinfo Texinfo source text
+0 string This\ is\ Info\ file GNU Info text
+
+# TeX documents, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 string \\input TeX document text
+0 string \\section LaTeX document text
+0 string \\setlength LaTeX document text
+0 string \\documentstyle LaTeX document text
+0 string \\chapter LaTeX document text
diff --git a/usr.bin/file/Magdir/timezone b/usr.bin/file/Magdir/timezone
new file mode 100644
index 0000000..e47a371
--- /dev/null
+++ b/usr.bin/file/Magdir/timezone
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# timezone: file(1) magic for timezone data
+#
+# from Daniel Quinlan (quinlan@yggdrasil.com)
+# this should work on Linux, SunOS, and maybe others
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0 timezone data
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0 timezone data
+0 string \0\0\0\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 timezone data
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0 timezone data
+0 string \0\0\0\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\0 timezone data
+0 string \0\0\0\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\0 timezone data
diff --git a/usr.bin/file/Magdir/troff b/usr.bin/file/Magdir/troff
new file mode 100644
index 0000000..ea75e85
--- /dev/null
+++ b/usr.bin/file/Magdir/troff
@@ -0,0 +1,27 @@
+
+#------------------------------------------------------------------------------
+# troff: file(1) magic for *roff
+#
+# updated by Daniel Quinlan (quinlan@yggdrasil.com)
+
+# troff input
+0 string .\\" troff or preprocessor input text
+0 string '\\" troff or preprocessor input text
+0 string '.\\" troff or preprocessor input text
+0 string \\" troff or preprocessor input text
+
+# ditroff intermediate output text
+0 string x\ T ditroff text
+>4 string cat for the C/A/T phototypesetter
+>4 string ps for PostScript
+>4 string dvi for DVI
+>4 string ascii for ASCII
+>4 string lj4 for LaserJet 4
+>4 string latin1 for ISO 8859-1 (Latin 1)
+>4 string X75 for xditview at 75dpi
+>>7 string -12 (12pt)
+>4 string X100 for xditview at 100dpi
+>>8 string -12 (12pt)
+
+# output data formats
+0 string \100\357 very old (C/A/T) troff output data
diff --git a/usr.bin/file/Magdir/typeset b/usr.bin/file/Magdir/typeset
new file mode 100644
index 0000000..2eda7c3
--- /dev/null
+++ b/usr.bin/file/Magdir/typeset
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# typeset: file(1) magic for other typesetting
+#
+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..843dc293
--- /dev/null
+++ b/usr.bin/file/Magdir/unknown
@@ -0,0 +1,36 @@
+
+#------------------------------------------------------------------------------
+# unknown: file(1) magic for unknown machines
+#
+# 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..7e88619
--- /dev/null
+++ b/usr.bin/file/Magdir/uuencode
@@ -0,0 +1,30 @@
+
+#------------------------------------------------------------------------------
+# uuencode: file(1) magic for ASCII-encoded files
+#
+
+# GRR: the first line of xxencoded files is identical to that in uuencoded
+# files, but the first character in most subsequent lines is 'h' instead of
+# 'M'. (xxencoding uses lowercase letters in place of most of uuencode's
+# punctuation and survives BITNET gateways better.) If regular expressions
+# were supported, this entry could possibly be split into two with
+# "begin\040\.\*\012M" or "begin\040\.\*\012h" (where \. and \* are REs).
+0 string begin\040 uuencoded or xxencoded text
+
+# btoa(1) is an alternative to uuencode that requires less space.
+0 string xbtoa\ Begin btoa'd text
+
+# ship(1) is another, much cooler alternative to uuencode.
+# Greg Roelofs, newt@uchicago.edu
+0 string $\012ship ship'd binary text
+
+# bencode(8) is used to encode compressed news batches (Bnews/Cnews only?)
+# Greg Roelofs, newt@uchicago.edu
+0 string Decode\ the\ following\ with\ bdeco bencoded News text
+
+# BinHex is the Macintosh ASCII-encoded file format (see also "apple")
+# Daniel Quinlan, quinlan@yggdrasil.com
+11 string must\ be\ converted\ with\ BinHex BinHex binary text
+>41 string x \b, version %.3s
+
+# GRR: is MIME BASE64 encoding handled somewhere?
diff --git a/usr.bin/file/Magdir/varied.out b/usr.bin/file/Magdir/varied.out
new file mode 100644
index 0000000..9245cfc
--- /dev/null
+++ b/usr.bin/file/Magdir/varied.out
@@ -0,0 +1,18 @@
+
+#------------------------------------------------------------------------------
+# varied.out: file(1) magic for various USG systems
+#
+# 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
+# AMD 29K
+0 beshort 0572 amd 29k coff noprebar executable
+0 beshort 01572 amd 29k coff prebar executable
+0 beshort 0160007 amd 29k coff archive
+# Cray
+6 beshort 0407 unicos (cray) executable
+# Ultrix 4.3
+596 string \130\337\377\377 Ultrix core file
+>600 string >\0 '%s'
diff --git a/usr.bin/file/Magdir/vax b/usr.bin/file/Magdir/vax
new file mode 100644
index 0000000..7dd86cc
--- /dev/null
+++ b/usr.bin/file/Magdir/vax
@@ -0,0 +1,34 @@
+
+#------------------------------------------------------------------------------
+# vax: file(1) magic for 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..4919964
--- /dev/null
+++ b/usr.bin/file/Magdir/visx
@@ -0,0 +1,31 @@
+
+#------------------------------------------------------------------------------
+# visx: file(1) magic for Visx format files
+#
+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/vms b/usr.bin/file/Magdir/vms
new file mode 100644
index 0000000..c91186f
--- /dev/null
+++ b/usr.bin/file/Magdir/vms
@@ -0,0 +1,27 @@
+
+#------------------------------------------------------------------------------
+# vms: file(1) magic for VMS executables (experimental)
+#
+# VMS .exe formats, both VAX and AXP (Greg Roelofs, newt@uchicago.edu)
+
+# GRR 950122: I'm just guessing on these, based on inspection of the headers
+# of three executables each for Alpha and VAX architectures. The VAX files
+# all had headers similar to this:
+#
+# 00000 b0 00 30 00 44 00 60 00 00 00 00 00 30 32 30 35 ..0.D.`.....0205
+# 00010 01 01 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 ................
+#
+0 string \xb0\0\x30\0 VMS VAX executable
+>44032 string PK\003\004 \b, Info-ZIP SFX archive v5.12 w/decryption
+#
+# The AXP files all looked like this, except that the byte at offset 0x22
+# was 06 in some of them and 07 in others:
+#
+# 00000 03 00 00 00 00 00 00 00 ec 02 00 00 10 01 00 00 ................
+# 00010 68 00 00 00 98 00 00 00 b8 00 00 00 00 00 00 00 h...............
+# 00020 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+# 00030 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
+# 00040 00 00 00 00 ff ff ff ff ff ff ff ff 02 00 00 00 ................
+#
+0 belong 0x03000000 VMS Alpha executable
+>75264 string PK\003\004 \b, Info-ZIP SFX archive v5.12 w/decryption
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/xenix b/usr.bin/file/Magdir/xenix
new file mode 100644
index 0000000..1acadec
--- /dev/null
+++ b/usr.bin/file/Magdir/xenix
@@ -0,0 +1,72 @@
+
+#------------------------------------------------------------------------------
+# xenix: file(1) magic for Microsoft Xenix
+#
+# "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
+>0x1e leshort &0x4000 V2.3
+>0x1e leshort &0x8000 V3.0
+>0x1c byte &0x4 86
+>0x1c byte &0xb 186
+>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/zilog b/usr.bin/file/Magdir/zilog
new file mode 100644
index 0000000..b746e20
--- /dev/null
+++ b/usr.bin/file/Magdir/zilog
@@ -0,0 +1,11 @@
+
+#------------------------------------------------------------------------------
+# zilog: file(1) magic for 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..12a6abd
--- /dev/null
+++ b/usr.bin/file/Magdir/zyxel
@@ -0,0 +1,16 @@
+
+#------------------------------------------------------------------------------
+# zyxel: file(1) magic for ZyXEL modems
+#
+# 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&0x0B 1 - ADPCM2 encoding
+>10 byte&0x0B 2 - ADPCM3 encoding
+>10 byte&0x0B 3 - ADPCM4 encoding
+>10 byte&0x0B 8 - New ADPCM3 encoding
+>10 byte&0x04 4 with resync
diff --git a/usr.bin/file/Makefile b/usr.bin/file/Makefile
new file mode 100644
index 0000000..5ca63c6
--- /dev/null
+++ b/usr.bin/file/Makefile
@@ -0,0 +1,56 @@
+# Makefile for file(1) cmd.
+# Copyright (c) Ian F. Darwin 86/09/01 - see LEGAL.NOTICE.
+# @(#)$Id: Makefile,v 1.10 1997/03/18 19:37:15 mpp 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).
+
+MAGICDIR= /usr/share/misc
+MAGICOWN= bin
+MAGICGRP= bin
+MAGICMODE= 444
+
+CFLAGS+= -DMAGIC='"$(MAGICDIR)/magic"' -DBUILTIN_ELF
+
+PROG= file
+SRCS= file.c apprentice.c fsmagic.c softmagic.c ascmagic.c \
+ compress.c is_tar.c readelf.c internat.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)
+
+beforeinstall:
+ $(INSTALL) $(COPY) -o $(MAGICOWN) -g $(MAGICGRP) -m $(MAGICMODE) \
+ magic $(DESTDIR)$(MAGICDIR)/magic
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/file/PORTING b/usr.bin/file/PORTING
new file mode 100644
index 0000000..1fa5da1
--- /dev/null
+++ b/usr.bin/file/PORTING
@@ -0,0 +1,76 @@
+Portability of the new file(1) command.
+@(#) $Id$
+
+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..2193292
--- /dev/null
+++ b/usr.bin/file/README
@@ -0,0 +1,91 @@
+** README for file(1) Command **
+@(#) $Id: README,v 1.1.1.2 1997/03/18 17:58:39 mpp Exp $
+
+This is Release 3.x of Ian Darwin's (copyright but distributable)
+file(1) command. This version is the standard "file" command for Linux,
+*BSD, 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, tar.h - 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
+readelf.[ch] - Standalone elf parsing code.
+compress.c - on-the-fly decompression.
+internat.c - recognize international `text' files.
+print.c - print results, errors, warnings.
+
+If your gzip sometimes fails to decompress things complaining about a short
+file, apply this patch [which is going to be in the next version of gzip]:
+*** - Tue Oct 29 02:06:35 1996
+--- util.c Sun Jul 21 21:51:38 1996
+*** 106,111 ****
+--- 108,114 ----
+
+ if (insize == 0) {
+ if (eof_ok) return EOF;
++ flush_window();
+ read_error();
+ }
+ bytes_in += (ulg)insize;
+
+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..9b358c1
--- /dev/null
+++ b/usr.bin/file/apprentice.c
@@ -0,0 +1,621 @@
+/*
+ * 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 <errno.h>
+#include "file.h"
+
+#ifndef lint
+static char *moduleid =
+ "@(#)$Id: apprentice.c,v 1.1.1.3 1997/03/18 17:58:42 mpp Exp $";
+#endif /* lint */
+
+#define EATAB {while (isascii((unsigned char) *l) && \
+ isspace((unsigned char) *l)) ++l;}
+#define LOWCASE(l) (isupper((unsigned char) (l)) ? \
+ tolower((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 void eatsize __P((char **));
+
+static int maxmagic = 0;
+
+static int apprentice_1 __P((char *, int));
+
+int
+apprentice(fn, check)
+char *fn; /* list of magic files */
+int check; /* non-zero? checking-only run. */
+{
+ char *p, *mfn;
+ int file_err, errs = -1;
+
+ maxmagic = MAXMAGIS;
+ magic = (struct magic *) calloc(sizeof(struct magic), maxmagic);
+ mfn = malloc(strlen(fn)+1);
+ if (magic == NULL || mfn == NULL) {
+ (void) fprintf(stderr, "%s: Out of memory.\n", progname);
+ if (check)
+ return -1;
+ else
+ exit(1);
+ }
+ fn = strcpy(mfn, fn);
+
+ while (fn) {
+ p = strchr(fn, ':');
+ if (p)
+ *p++ = '\0';
+ file_err = apprentice_1(fn, check);
+ if (file_err > errs)
+ errs = file_err;
+ fn = p;
+ }
+ if (errs == -1)
+ (void) fprintf(stderr, "%s: couldn't find any magic files!\n",
+ progname);
+ if (!check && errs)
+ exit(1);
+
+ free(mfn);
+ return errs;
+}
+
+static int
+apprentice_1(fn, check)
+char *fn; /* name of magic file */
+int check; /* non-zero? checking-only run. */
+{
+ static const char hdr[] =
+ "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
+ FILE *f;
+ char line[BUFSIZ+1];
+ int errs = 0;
+
+ f = fopen(fn, "r");
+ if (f==NULL) {
+ if (errno != ENOENT)
+ (void) fprintf(stderr,
+ "%s: can't read magic file %s (%s)\n",
+ progname, fn, strerror(errno));
+ return -1;
+ }
+
+ /* parse it */
+ if (check) /* print silly verbose header for USG compat. */
+ (void) printf("%s\n", hdr);
+
+ 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 = 1;
+ }
+
+ (void) fclose(f);
+ return errs;
+}
+
+/*
+ * extend the sign bit if the comparison is to be signed
+ */
+uint32
+signextend(m, v)
+struct magic *m;
+uint32 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 = (int32) 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;
+
+#define ALLOC_INCR 20
+ if (nd+1 >= maxmagic){
+ maxmagic += ALLOC_INCR;
+ 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);
+ }
+ memset(&magic[*ndx], 0, sizeof(struct magic) * ALLOC_INCR);
+ }
+ 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;
+ }
+ if (m->cont_level != 0 && *l == '&') {
+ ++l; /* step over */
+ m->flag |= ADD;
+ }
+
+ /* get offset, then skip over it */
+ m->offset = (int) strtoul(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 == '.') {
+ l++;
+ switch (LOWCASE(*l)) {
+ case 'l':
+ m->in.type = LONG;
+ break;
+ case 'h':
+ case 's':
+ m->in.type = SHORT;
+ break;
+ case 'c':
+ 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 = strtoul(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, strtoul(l, &l, 0));
+ eatsize(&l);
+ } 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, strtoul(*p, p, 0));
+ eatsize(p);
+ }
+ 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 2 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;
+ 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;
+ }
+ }
+ }
+}
+
+/*
+ * eatsize(): Eat the size spec from a number [eg. 10UL]
+ */
+static void
+eatsize(p)
+char **p;
+{
+ char *l = *p;
+
+ if (LOWCASE(*l) == 'u')
+ l++;
+
+ switch (LOWCASE(*l)) {
+ case 'l': /* long */
+ case 's': /* short */
+ case 'h': /* short */
+ case 'b': /* char/byte */
+ case 'c': /* char/byte */
+ l++;
+ /*FALLTHROUGH*/
+ default:
+ break;
+ }
+
+ *p = l;
+}
diff --git a/usr.bin/file/ascmagic.c b/usr.bin/file/ascmagic.c
new file mode 100644
index 0000000..52f5090
--- /dev/null
+++ b/usr.bin/file/ascmagic.c
@@ -0,0 +1,124 @@
+/*
+ * 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.3 1997/03/18 17:58:46 mpp 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, has_escapes = 0;
+ unsigned char *s;
+ char nbuf[HOWMANY+1]; /* one extra for terminating '\0' */
+ char *token;
+ register struct names *p;
+
+ /*
+ * Do the tar test first, because if the first file in the tar
+ * archive starts with a dot, we can confuse it with an nroff file.
+ */
+ switch (is_tar(buf, nbytes)) {
+ case 1:
+ ckfputs("tar archive", stdout);
+ return 1;
+ case 2:
+ ckfputs("POSIX tar archive", stdout);
+ return 1;
+ }
+
+ /*
+ * 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[1] == '"'))) {
+ 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;
+ }
+
+
+ /* Make sure we are dealing with ascii text before looking for tokens */
+ for (i = 0; i < nbytes; i++) {
+ if (!isascii(buf[i]))
+ return 0; /* not all ASCII */
+ }
+
+ /* 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;
+ }
+ }
+ }
+
+ /* 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..95b3f9a
--- /dev/null
+++ b/usr.bin/file/compress.c
@@ -0,0 +1,122 @@
+/*
+ * 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$
+ */
+#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 }, /* compressed */
+ { "\037\213", 2, { "gzip", "-cdq", NULL }, 1 }, /* gzipped */
+ { "\037\236", 2, { "gzip", "-cdq", NULL }, 1 }, /* frozen */
+ { "\037\240", 2, { "gzip", "-cdq", NULL }, 1 }, /* SCO LZH */
+ /* the standard pack utilities do not accept standard input */
+ { "\037\036", 2, { "gzip", "-cdq", NULL }, 0 }, /* packed */
+};
+
+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..f9818fa
--- /dev/null
+++ b/usr.bin/file/cvsimport.sh
@@ -0,0 +1,18 @@
+#!/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: cvsimport.sh,v 1.3 1997/02/22 19:29:10 peter Exp $
+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
+mv Magdir/msdos Magdir/ms-dos
+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..ba28b3f
--- /dev/null
+++ b/usr.bin/file/file.1
@@ -0,0 +1,366 @@
+.TH FILE 1 "Copyright but distributable"
+.\" $Id: file.1,v 1.9 1997/03/18 19:37:17 mpp Exp $
+.SH NAME
+file \- determine file type
+.SH SYNOPSIS
+.B file
+[
+.B \-vczL
+]
+[
+.B \-f
+namefile ]
+[
+.B \-m
+magicfiles ]
+file ...
+.SH DESCRIPTION
+This manual page documents version 3.22 of the
+.B file
+command.
+.B 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
+.SM ASCII
+characters and is probably safe to read on an
+.SM 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 /usr/share/misc/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
+.BR 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
+.IR 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)
+.I a.out
+file, whose format is defined in
+.I a.out.h
+and possibly
+.I 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 /usr/share/misc/magic.
+.PP
+If an argument appears to be an
+.SM ASCII
+file,
+.B file
+attempts to guess its language.
+The language tests look for particular strings (cf
+.IR names.h )
+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
+.BR troff (1)
+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
+.BR tar (1)
+archives) and determine whether an unknown file should be
+labelled as `ascii text' or `data'.
+.SH OPTIONS
+.TP 8
+.B \-v
+Print the version of the program and exit.
+.TP 8
+.B \-m list
+Specify an alternate list of files containing magic numbers.
+This can be a single file, or a colon-separated list of files.
+.TP 8
+.B \-z
+Try to look inside compressed files.
+.TP 8
+.B \-c
+Cause 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.
+.TP 8
+.B \-f namefile
+Read the names of the files to be examined from
+.I namefile
+(one per line)
+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.
+.TP 8
+.B \-L
+option causes symlinks to be followed, as the like-named option in
+.BR ls (1).
+(on systems that support symbolic links).
+.SH FILES
+.I /usr/share/misc/magic
+\- default list of magic numbers (used to be
+.I /etc/magic
+in previous versions of FreeBSD)
+.SH ENVIRONMENT
+The environment variable
+.B MAGIC
+can be used to set the default magic number files.
+.SH SEE ALSO
+.BR magic (5)
+\- description of magic file format.
+.br
+.BR 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 behavior 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
+.BR 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
+.B file
+command uses a magic file,
+keep the old magic file around for comparison purposes
+(rename it to
+.IR /usr/share/misc/magic.orig ).
+.SH HISTORY
+There has been a
+.B file
+command in every \s-1UNIX\s0 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
+.B 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
+.BR file .
+.RE
+.PP
+Changes by Ian Darwin and various authors including
+Christos Zoulas (christos@deshaw.com), 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
+.B 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,
+.BR ndbm (3)
+or, better yet, fixed-length
+.SM 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
+.B File
+uses several algorithms that favor speed over accuracy,
+thus it can be misled about the contents of
+.SM ASCII
+files.
+.PP
+The support for
+.SM 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
+.SM "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.,
+.BR troff (1)
+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 optimization 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 ftp.deshaw.com
+in the directory
+.I /pub/file/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..a5fe4b9
--- /dev/null
+++ b/usr.bin/file/file.c
@@ -0,0 +1,398 @@
+/*
+ * 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.7 1997/03/18 19:37:18 mpp 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() */
+#if (__COHERENT__ >= 0x420)
+# include <sys/utime.h>
+#else
+# ifdef USE_UTIMES
+# include <sys/time.h>
+# else
+# include <utime.h>
+# endif
+#endif
+#include <unistd.h> /* for read() */
+
+#include <netinet/in.h> /* for byte swapping */
+
+#include "patchlevel.h"
+#include "file.h"
+
+#ifdef S_IFLNK
+# define USAGE "Usage: %s [-vczL] [-f namefile] [-m magicfiles] file...\n"
+#else
+# define USAGE "Usage: %s [-vcz] [-f namefile] [-m magicfiles] 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; /* where magic be found */
+
+char *progname; /* used throughout */
+int lineno; /* line number in the magic file */
+
+
+static void unwrap __P((char *fn));
+#if 0
+static int byteconv4 __P((int, int, int));
+static short byteconv2 __P((int, int, int));
+#endif
+
+/*
+ * 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, app = 0;
+
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ if (!(magicfile = getenv("MAGIC")))
+ magicfile = MAGIC;
+
+ while ((c = getopt(argc, argv, "vcdf:Lm:z")) != -1)
+ switch (c) {
+ case 'v':
+ (void) fprintf(stdout, "%s-%d.%d\n", progname,
+ FILE_VERSION_MAJOR, patchlevel);
+ return 1;
+ case 'c':
+ ++check;
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'f':
+ if (!app) {
+ ret = apprentice(magicfile, check);
+ if (check)
+ exit(ret);
+ app = 1;
+ }
+ 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);
+ }
+
+ if (!app) {
+ ret = apprentice(magicfile, check);
+ if (check)
+ exit(ret);
+ app = 1;
+ }
+
+ 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 (strcmp("-", fn) == 0) {
+ f = stdin;
+ wid = 1;
+ } else {
+ 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);
+}
+
+
+#if 0
+/*
+ * byteconv4
+ * Input:
+ * from 4 byte quantity to convert
+ * same whether to perform byte swapping
+ * big_endian whether we are a big endian host
+ */
+static int
+byteconv4(from, same, big_endian)
+ int from;
+ int same;
+ int big_endian;
+{
+ if (same)
+ return from;
+ else if (big_endian) /* lsb -> msb conversion on msb */
+ {
+ union {
+ int i;
+ char c[4];
+ } retval, tmpval;
+
+ tmpval.i = from;
+ retval.c[0] = tmpval.c[3];
+ retval.c[1] = tmpval.c[2];
+ retval.c[2] = tmpval.c[1];
+ retval.c[3] = tmpval.c[0];
+
+ return retval.i;
+ }
+ else
+ return ntohl(from); /* msb -> lsb conversion on lsb */
+}
+
+/*
+ * byteconv2
+ * Same as byteconv4, but for shorts
+ */
+static short
+byteconv2(from, same, big_endian)
+ int from;
+ int same;
+ int big_endian;
+{
+ if (same)
+ return from;
+ else if (big_endian) /* lsb -> msb conversion on msb */
+ {
+ union {
+ short s;
+ char c[2];
+ } retval, tmpval;
+
+ tmpval.s = (short) from;
+ retval.c[0] = tmpval.c[1];
+ retval.c[1] = tmpval.c[0];
+
+ return retval.s;
+ }
+ else
+ return ntohs(from); /* msb -> lsb conversion on lsb */
+}
+#endif
+
+/*
+ * 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 stat sb;
+ int nbytes = 0; /* number of bytes read from a datafile */
+ char match = '\0';
+
+ 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 */
+ match = tryit(buf, nbytes, zflag);
+ }
+
+#ifdef BUILTIN_ELF
+ if (match == 's' && nbytes > 5)
+ tryelf(fd, buf, nbytes);
+#endif
+
+ if (inname != stdname) {
+#ifdef RESTORE_TIME
+ /*
+ * Try to restore access, modification times if read it.
+ */
+# ifdef USE_UTIMES
+ struct timeval utsbuf[2];
+ utsbuf[0].tv_sec = sb.st_atime;
+ utsbuf[1].tv_sec = sb.st_mtime;
+
+ (void) utimes(inname, utsbuf); /* don't care if loses */
+# else
+ struct utimbuf utbuf;
+
+ utbuf.actime = sb.st_atime;
+ utbuf.modtime = sb.st_mtime;
+ (void) utime(inname, &utbuf); /* don't care if loses */
+# endif
+#endif
+ (void) close(fd);
+ }
+ (void) putchar('\n');
+}
+
+
+int
+tryit(buf, nb, zflag)
+unsigned char *buf;
+int nb, zflag;
+{
+ /* try compression stuff */
+ if (zflag && zmagic(buf, nb))
+ return 'z';
+
+ /* try tests in /etc/magic (or surrogate magic file) */
+ if (softmagic(buf, nb))
+ return 's';
+
+ /* try known keywords, check whether it is ASCII */
+ if (ascmagic(buf, nb))
+ return 'a';
+
+ /* see if it's international language text */
+ if (internatmagic(buf, nb))
+ return 'i';
+
+ /* abandon hope, all ye who remain here */
+ ckfputs("data", stdout);
+ return '\0';
+}
diff --git a/usr.bin/file/file.h b/usr.bin/file/file.h
new file mode 100644
index 0000000..bdca97b
--- /dev/null
+++ b/usr.bin/file/file.h
@@ -0,0 +1,151 @@
+/*
+ * file.h - definitions for file(1) program
+ * @(#)$Id: file.h,v 1.1.1.3 1997/03/18 17:58:51 mpp 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.
+ */
+
+#ifndef __file_h__
+#define __file_h__
+
+typedef int int32;
+typedef unsigned int uint32;
+
+#ifndef HOWMANY
+# define HOWMANY 8192 /* how much of the file to look at */
+#endif
+#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 */
+#define ADD 4 /* if '>&' appears, */
+ short cont_level; /* level of ">" */
+ struct {
+ char type; /* byte short long */
+ int32 offset; /* offset from indirection */
+ } in;
+ int32 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;
+ uint32 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 */
+ uint32 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 internatmagic __P((unsigned char *, int));
+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 int tryit __P((unsigned char *, int, int));
+extern int zmagic __P((unsigned char *, int));
+extern void ckfprintf __P((FILE *, const char *, ...));
+extern uint32 signextend __P((struct magic *, unsigned int32));
+extern int internatmagic __P((unsigned char *, int));
+extern void tryelf __P((int, char *, int));
+
+
+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(sun) || defined(__sun__) || defined (__sun)
+# if defined(__svr4) || defined (__SVR4) || defined(__svr4__)
+# define SOLARIS
+# else
+# define SUNOS
+# endif
+#endif
+
+
+#if !defined(__STDC__) || defined(SUNOS) || defined(__convex__)
+extern int sys_nerr;
+extern char *sys_errlist[];
+#define strerror(e) \
+ (((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown error")
+#define strtoul(a, b, c) strtol(a, b, c)
+#endif
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 512
+#endif
+
+#endif /* __file_h__ */
diff --git a/usr.bin/file/fsmagic.c b/usr.bin/file/fsmagic.c
new file mode 100644
index 0000000..056d3ea
--- /dev/null
+++ b/usr.bin/file/fsmagic.c
@@ -0,0 +1,181 @@
+/*
+ * 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 defined(__SVR4) || defined(_SVR4_SOURCE)
+# include <sys/mkdev.h>
+# endif
+#endif
+#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.3 1997/03/18 17:58:44 mpp 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 (%ld/%ld)",
+ (long) major(sb->st_rdev), (long) minor(sb->st_rdev));
+ return 1;
+ case S_IFBLK:
+ (void) printf("block special (%ld/%ld)",
+ (long) major(sb->st_rdev), (long) 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
+#ifndef __COHERENT__
+ case S_IFSOCK:
+ ckfputs("socket", stdout);
+ return 1;
+#endif
+#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/internat.c b/usr.bin/file/internat.c
new file mode 100644
index 0000000..59a508a
--- /dev/null
+++ b/usr.bin/file/internat.c
@@ -0,0 +1,72 @@
+#include "file.h"
+
+#include <string.h>
+
+#define F 0
+#define T 1
+
+/*
+ * List of characters that look "reasonable" in international
+ * language texts. That's almost all characters :), except a
+ * few in the control range of ASCII (all the known international
+ * charactersets share the bottom half with ASCII).
+ */
+static char maybe_internat[256] = {
+ F, F, F, F, F, F, F, F, T, T, T, T, T, T, F, F, /* 0x0X */
+ F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F, /* 0x1X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x2X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x3X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x4X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x5X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x6X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F, /* 0x7X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x8X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x9X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0xaX */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0xbX */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0xcX */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0xdX */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0xeX */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T /* 0xfX */
+};
+
+/* Maximal length of a line we consider "reasonable". */
+#define MAXLINELEN 300
+
+int
+internatmagic(buf, nbytes)
+ unsigned char *buf;
+ int nbytes;
+{
+ int i;
+ unsigned char *cp;
+
+ nbytes--;
+
+ /* First, look whether there are "unreasonable" characters. */
+ for (i = 0, cp = buf; i < nbytes; i++, cp++)
+ if (!maybe_internat[*cp])
+ return 0;
+
+ /*
+ * Now, look whether the file consists of lines of
+ * "reasonable" length.
+ */
+
+ for (i = 0; i < nbytes;) {
+ cp = memchr(buf, '\n', nbytes - i);
+ if (cp == NULL) {
+ /* Don't fail if we hit the end of buffer. */
+ if (i + MAXLINELEN >= nbytes)
+ break;
+ else
+ return 0;
+ }
+ if (cp - buf > MAXLINELEN)
+ return 0;
+ i += (cp - buf + 1);
+ buf = cp + 1;
+ }
+ ckfputs("International language text", stdout);
+ return 1;
+}
diff --git a/usr.bin/file/is_tar.c b/usr.bin/file/is_tar.c
new file mode 100644
index 0000000..7008a40
--- /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.2 1997/03/18 17:58:48 mpp 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 int from_oct(int, char*); /* Decode octal number */
+#else
+static int 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 int 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 int
+from_oct(digs, where)
+ register int digs;
+ register char *where;
+{
+ register int 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..bd1a5a5
--- /dev/null
+++ b/usr.bin/file/magic.5
@@ -0,0 +1,206 @@
+.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
+This manual page documents the format of the magic file as
+used by the
+.BR file (1)
+command, version 3.22. The
+.B file
+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
+.I /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,
+.BR ^ ,
+to specify that the value from the file must have clear any of the bits
+that are set in the specified value, or
+.BR x ,
+to specify that any value will match. If the character is omitted,
+it is assumed to be
+.BR = .
+.IP
+Numeric values are specified in C form; e.g.
+.B 13
+is decimal,
+.B 013
+is octal, and
+.B 0x13
+is hexadecimal.
+.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
+.BR 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 an 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.
+.PP
+Sometimes you do not know the exact offset as this depends on the length of
+preceding fields. You can specify an offset relative to the end of the
+last uplevel field (of course this may only be done for sublevel tests, i.e.
+test beginning with
+.B >
+). Such a relative offset is specified using
+.B &
+as a prefix to the offset.
+.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-dependent; 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
+.BR 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.5,v 1.1.1.3 1997/03/18 17:58:57 mpp Exp $
diff --git a/usr.bin/file/names.h b/usr.bin/file/names.h
new file mode 100644
index 0000000..1fed1bf
--- /dev/null
+++ b/usr.bin/file/names.h
@@ -0,0 +1,99 @@
+/*
+ * 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.1.1.3 1997/03/18 17:58:52 mpp 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_CC 1 /* Bjarne's postincrement */
+#define L_FORT 2 /* the oldest one */
+#define L_MAKE 3 /* Makefiles */
+#define L_PLI 4 /* PL/1 */
+#define L_MACH 5 /* some kinda assembler */
+#define L_ENG 6 /* English */
+#define L_PAS 7 /* Pascal */
+#define L_MAIL 8 /* Electronic mail */
+#define L_NEWS 9 /* Usenet Netnews */
+
+static char *types[] = {
+ "C program text",
+ "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_CC},
+ {"template", L_CC},
+ {"virtual", L_CC},
+ {"class", L_CC},
+ {"public:", L_CC},
+ {"private:", L_CC},
+ {"/*", L_C}, /* must precede "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},
+ {".text", 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..620a57f
--- /dev/null
+++ b/usr.bin/file/patchlevel.h
@@ -0,0 +1,146 @@
+#define FILE_VERSION_MAJOR 3
+#define patchlevel 22
+
+/*
+ * Patchlevel file for Ian Darwin's MAGIC command.
+ * $Id: patchlevel.h,v 1.1.1.3 1997/03/18 17:58:54 mpp Exp $
+ *
+ * $Log: patchlevel.h,v $
+ * Revision 1.6 1997/02/22 19:54:59 peter
+ * Revert $FreeBSD$ to $Id$
+ *
+ * Revision 1.5 1997/02/22 19:29:15 peter
+ * Revert $Id: patchlevel.h,v 1.6 1997/02/22 19:54:59 peter Exp $ to $Id: patchlevel.h,v 1.6 1997/02/22 19:54:59 peter Exp $
+ *
+ * Revision 1.4 1997/01/14 06:59:48 jkh
+ * Make the long-awaited change from $Id: patchlevel.h,v 1.6 1997/02/22 19:54:59 peter Exp $ to $Id: patchlevel.h,v 1.6 1997/02/22 19:54:59 peter Exp $
+ *
+ * This will make a number of things easier in the future, as well as (finally!)
+ * avoiding the Id-smashing problem which has plagued developers for so long.
+ *
+ * Boy, I'm glad we're not using sup anymore. This update would have been
+ * insane otherwise.
+ *
+ * Revision 1.3 1996/01/23 12:40:20 mpp
+ * Merged changes to resolve conflicts with file 3.19 import.
+ *
+ * Revision 1.2 1995/05/30 06:30:06 rgrimes
+ * Remove trailing whitespace.
+ *
+ * Revision 1.1.1.2 1996/01/22 22:31:44 mpp
+ * Upgrade to file version 3.19.
+ *
+ * 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.1.1.3 1997/03/18 17:58:54 mpp
+ * Upgrade to file version 3.22.
+ *
+ * Obtained from: ftp://ftp.deshaw.com/pub/file/file-3.22.tar.gz
+ *
+ * Revision 1.22 1997/01/15 17:23:24 christos
+ * - add support for elf core files: find the program name under SVR4 [Ken Pizzini]
+ * - print strings only up to the first carriage return [various]
+ * - freebsd international ascii support [J Wunsch]
+ * - magic fixes and additions [Guy Harris]
+ * - 64 bit fixes [Larry Schwimmer]
+ * - support for both utime and utimes, but don't restore file access times
+ * by default [various]
+ * - \xXX only takes 2 hex digits, not 3.
+ * - re-implement support for core files [Guy Harris]
+ *
+ * Revision 1.21 1996/10/05 18:15:29 christos
+ * Segregate elf stuff and conditionally enable it with -DBUILTIN_ELF
+ * More magic fixes
+ *
+ * Revision 1.20 1996/06/22 22:15:52 christos
+ * - support relative offsets of the form >&
+ * - fix bug with truncating magic strings that contain \n
+ * - file -f - did not read from stdin as documented
+ * - support elf file parsing using our own elf support.
+ * - as always magdir fixes and additions.
+ *
+ * Revision 1.19 1995/10/27 23:14:46 christos
+ * Ability to parse colon separated list of magic files
+ * New LEGAL.NOTICE
+ * Various magic file changes
+ *
+ * Revision 1.18 1995/05/20 22:09:21 christos
+ * Passed incorrect argument to eatsize().
+ * Use %ld and %lx where appropriate.
+ * Remove unused variables
+ * ELF support for both big and little endian
+ * Fixes for small files again.
+ *
+ * Revision 1.17 1995/04/28 17:29:13 christos
+ * - Incorrect nroff detection fix from der Mouse
+ * - Lost and incorrect magic entries.
+ * - Added ELF stripped binary detection [in C; ugh]
+ * - Look for $MAGIC to find the magic file.
+ * - Eat trailing size specifications from numbers i.e. ignore 10L
+ * - More fixes for very short files
+ *
+ * Revision 1.16 1995/03/25 22:06:45 christos
+ * - use strtoul() where it exists.
+ * - fix sign-extend bug
+ * - try to detect tar archives before nroff files, otherwise
+ * tar files where the first file starts with a . will not work
+ *
+ * Revision 1.15 1995/01/21 21:03:35 christos
+ * Added CSECTION for the file man page
+ * Added version flag -v
+ * Fixed bug with -f input flag (from iorio@violet.berkeley.edu)
+ * Lots of magic fixes and reorganization...
+ *
+ * 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..09c3fc8
--- /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.3 1997/03/18 17:58:49 mpp 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/readelf.c b/usr.bin/file/readelf.c
new file mode 100644
index 0000000..2ae533b
--- /dev/null
+++ b/usr.bin/file/readelf.c
@@ -0,0 +1,318 @@
+
+#ifdef BUILTIN_ELF
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "readelf.h"
+#include "file.h"
+
+static void doshn __P((int, off_t, int, size_t, char *));
+static void dophn_exec __P((int, off_t, int, size_t, char *));
+static void dophn_core __P((int, off_t, int, size_t, char *));
+
+static void
+doshn(fd, off, num, size, buf)
+ int fd;
+ off_t off;
+ int num;
+ size_t size;
+ char *buf;
+{
+ /*
+ * This works for both 32-bit and 64-bit ELF formats,
+ * because it looks only at the "sh_type" field, which is
+ * always 32 bits, and is preceded only by the "sh_name"
+ * field which is also always 32 bits, and because it uses
+ * the shdr size from the ELF header rather than using
+ * the size of an "Elf32_Shdr".
+ */
+ Elf32_Shdr *sh = (Elf32_Shdr *) buf;
+
+ if (lseek(fd, off, SEEK_SET) == -1)
+ error("lseek failed (%s).\n", strerror(errno));
+
+ for ( ; num; num--) {
+ if (read(fd, buf, size) == -1)
+ error("read failed (%s).\n", strerror(errno));
+ if (sh->sh_type == SHT_SYMTAB) {
+ (void) printf (", not stripped");
+ return;
+ }
+ }
+ (void) printf (", stripped");
+}
+
+/*
+ * Look through the program headers of an executable image, searching
+ * for a PT_INTERP section; if one is found, it's dynamically linked,
+ * otherwise it's statically linked.
+ */
+static void
+dophn_exec(fd, off, num, size, buf)
+ int fd;
+ off_t off;
+ int num;
+ size_t size;
+ char *buf;
+{
+ /* I am not sure if this works for 64 bit elf formats */
+ Elf32_Phdr *ph = (Elf32_Phdr *) buf;
+
+ if (lseek(fd, off, SEEK_SET) == -1)
+ error("lseek failed (%s).\n", strerror(errno));
+
+ for ( ; num; num--) {
+ if (read(fd, buf, size) == -1)
+ error("read failed (%s).\n", strerror(errno));
+ if (ph->p_type == PT_INTERP) {
+ /*
+ * Has an interpreter - must be a dynamically-linked
+ * executable.
+ */
+ printf(", dynamically linked");
+ return;
+ }
+ }
+ printf(", statically linked");
+}
+
+size_t prpsoffsets[] = {
+ 100, /* SunOS 5.x */
+ 32, /* Linux */
+};
+
+#define NOFFSETS (sizeof prpsoffsets / sizeof prpsoffsets[0])
+
+/*
+ * Look through the program headers of an executable image, searching
+ * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE"; if one
+ * is found, try looking in various places in its contents for a 16-character
+ * string containing only printable characters - if found, that string
+ * should be the name of the program that dropped core.
+ * Note: right after that 16-character string is, at least in SunOS 5.x
+ * (and possibly other SVR4-flavored systems) and Linux, a longer string
+ * (80 characters, in 5.x, probably other SVR4-flavored systems, and Linux)
+ * containing the start of the command line for that program.
+ */
+static void
+dophn_core(fd, off, num, size, buf)
+ int fd;
+ off_t off;
+ int num;
+ size_t size;
+ char *buf;
+{
+ /*
+ * This doesn't work for 64-bit ELF, as the "p_offset" field is
+ * 64 bits in 64-bit ELF.
+ */
+ /*
+ * This doesn't work for 64-bit ELF, as the "p_offset" field is
+ * 64 bits in 64-bit ELF.
+ */
+ Elf32_Phdr *ph = (Elf32_Phdr *) buf;
+ Elf32_Nhdr *nh;
+ size_t offset, noffset, reloffset;
+ unsigned char c;
+ int i, j;
+ char nbuf[BUFSIZ];
+ int bufsize;
+
+ for ( ; num; num--) {
+ if (lseek(fd, off, SEEK_SET) == -1)
+ error("lseek failed (%s).\n", strerror(errno));
+ if (read(fd, buf, size) == -1)
+ error("read failed (%s).\n", strerror(errno));
+ off += size;
+ if (ph->p_type != PT_NOTE)
+ continue;
+ if (lseek(fd, ph->p_offset, SEEK_SET) == -1)
+ error("lseek failed (%s).\n", strerror(errno));
+ bufsize = read(fd, nbuf, BUFSIZ);
+ if (bufsize == -1)
+ error("read failed (%s).\n", strerror(errno));
+ offset = 0;
+ for (;;) {
+ if (offset >= bufsize)
+ break;
+ nh = (Elf32_Nhdr *)&nbuf[offset];
+ offset += sizeof *nh;
+
+ /*
+ * If this note isn't an NT_PRPSINFO note, it's
+ * not what we're looking for.
+ */
+ if (nh->n_type != NT_PRPSINFO) {
+ offset += nh->n_namesz;
+ offset = ((offset + 3)/4)*4;
+ offset += nh->n_descsz;
+ offset = ((offset + 3)/4)*4;
+ continue;
+ }
+
+ /*
+ * Make sure this note has the name "CORE".
+ */
+ if (offset + nh->n_namesz >= bufsize) {
+ /*
+ * We're past the end of the buffer.
+ */
+ break;
+ }
+ if (nh->n_namesz != 5
+ || strcmp(&nbuf[offset], "CORE") != 0)
+ continue;
+ offset += nh->n_namesz;
+ offset = ((offset + 3)/4)*4;
+
+ /*
+ * Extract the program name. We assume it to be
+ * 16 characters (that's what it is in SunOS 5.x
+ * and Linux).
+ *
+ * Unfortunately, it's at a different offset in
+ * SunOS 5.x and Linux, so try multiple offsets.
+ * If the characters aren't all printable, reject
+ * it.
+ */
+ for (i = 0; i < NOFFSETS; i++) {
+ reloffset = prpsoffsets[i];
+ noffset = offset + reloffset;
+ for (j = 0; j < 16;
+ j++, noffset++, reloffset++) {
+ /*
+ * Make sure we're not past the end
+ * of the buffer; if we are, just
+ * give up.
+ */
+ if (noffset >= bufsize)
+ return;
+
+ /*
+ * Make sure we're not past the
+ * end of the contents; if we
+ * are, this obviously isn't
+ * the right offset.
+ */
+ if (reloffset >= nh->n_descsz)
+ goto tryanother;
+
+ c = nbuf[noffset];
+ if (c != '\0' && !isprint(c))
+ goto tryanother;
+ }
+
+ /*
+ * Well, that worked.
+ */
+ printf(", from '%.16s'",
+ &nbuf[offset + prpsoffsets[i]]);
+ return;
+
+ tryanother:
+ ;
+ }
+ offset += nh->n_descsz;
+ offset = ((offset + 3)/4)*4;
+ }
+ }
+}
+
+void
+tryelf(fd, buf, nbytes)
+ int fd;
+ char *buf;
+ int nbytes;
+{
+ union {
+ int32 l;
+ char c[sizeof (int32)];
+ } u;
+
+ /*
+ * ELF executables have multiple section headers in arbitrary
+ * file locations and thus file(1) cannot determine it from easily.
+ * Instead we traverse thru all section headers until a symbol table
+ * one is found or else the binary is stripped.
+ */
+ if (buf[EI_MAG0] != ELFMAG0 || buf[EI_MAG1] != ELFMAG1
+ || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3)
+ return;
+
+
+ if (buf[4] == ELFCLASS32) {
+ Elf32_Ehdr elfhdr;
+ if (nbytes <= sizeof (Elf32_Ehdr))
+ return;
+
+
+ u.l = 1;
+ (void) memcpy(&elfhdr, buf, sizeof elfhdr);
+ /*
+ * If the system byteorder does not equal the
+ * object byteorder then don't test.
+ * XXX - we could conceivably fix up the "dophn_XXX()" and
+ * "doshn()" routines to extract stuff in the right
+ * byte order....
+ */
+ if ((u.c[sizeof(long) - 1] + 1) == elfhdr.e_ident[5]) {
+ if (elfhdr.e_type == ET_CORE)
+ dophn_core(fd, elfhdr.e_phoff, elfhdr.e_phnum,
+ elfhdr.e_phentsize, buf);
+ else {
+ if (elfhdr.e_type == ET_EXEC) {
+ dophn_exec(fd, elfhdr.e_phoff,
+ elfhdr.e_phnum,
+ elfhdr.e_phentsize, buf);
+ }
+ doshn(fd, elfhdr.e_shoff, elfhdr.e_shnum,
+ elfhdr.e_shentsize, buf);
+ }
+ }
+ return;
+ }
+
+ if (buf[4] == ELFCLASS64) {
+ Elf64_Ehdr elfhdr;
+ if (nbytes <= sizeof (Elf64_Ehdr))
+ return;
+
+
+ u.l = 1;
+ (void) memcpy(&elfhdr, buf, sizeof elfhdr);
+
+ /*
+ * If the system byteorder does not equal the
+ * object byteorder then don't test.
+ * XXX - we could conceivably fix up the "dophn_XXX()" and
+ * "doshn()" routines to extract stuff in the right
+ * byte order....
+ */
+ if ((u.c[sizeof(long) - 1] + 1) == elfhdr.e_ident[5]) {
+#ifdef notyet
+ if (elfhdr.e_type == ET_CORE)
+ dophn_core(fd, elfhdr.e_phoff, elfhdr.e_phnum,
+ elfhdr.e_phentsize, buf);
+ else
+#endif
+ {
+#ifdef notyet
+ if (elfhdr.e_type == ET_EXEC) {
+ dophn_exec(fd, elfhdr.e_phoff,
+ elfhdr.e_phnum,
+ elfhdr.e_phentsize, buf);
+ }
+#endif
+ doshn(fd, elfhdr.e_shoff, elfhdr.e_shnum,
+ elfhdr.e_shentsize, buf);
+ }
+ }
+ return;
+ }
+}
+#endif
diff --git a/usr.bin/file/readelf.h b/usr.bin/file/readelf.h
new file mode 100644
index 0000000..853eed6
--- /dev/null
+++ b/usr.bin/file/readelf.h
@@ -0,0 +1,167 @@
+/*
+ * readelf.h
+ * @(#)$Id: readelf.h,v 1.1.1.1 1997/03/18 17:58:55 mpp Exp $
+ *
+ * Provide elf data structures for non-elf machines, allowing file
+ * non-elf hosts to determine if an elf binary is stripped.
+ * Note: cobbled from the linux header file, with modifications
+ */
+#ifndef __fake_elf_h__
+#define __fake_elf_h__
+
+typedef unsigned int Elf32_Addr;
+typedef unsigned short Elf32_Half;
+typedef unsigned int Elf32_Off;
+typedef unsigned int Elf32_Word;
+typedef unsigned char Elf32_Char;
+
+/* XXX: We need 64 bit numbers here */
+typedef u_quad_t Elf64_Addr;
+typedef unsigned short Elf64_Half;
+typedef u_quad_t Elf64_Off;
+typedef unsigned int Elf64_Word;
+typedef unsigned char Elf64_Char;
+
+#define EI_NIDENT 16
+
+typedef struct {
+ Elf32_Char e_ident[EI_NIDENT];
+ Elf32_Half e_type;
+ Elf32_Half e_machine;
+ Elf32_Word e_version;
+ Elf32_Addr e_entry; /* Entry point */
+ Elf32_Off e_phoff;
+ Elf32_Off e_shoff;
+ Elf32_Word e_flags;
+ Elf32_Half e_ehsize;
+ Elf32_Half e_phentsize;
+ Elf32_Half e_phnum;
+ Elf32_Half e_shentsize;
+ Elf32_Half e_shnum;
+ Elf32_Half e_shstrndx;
+} Elf32_Ehdr;
+
+typedef struct {
+ Elf64_Char e_ident[EI_NIDENT];
+ Elf64_Half e_type;
+ Elf64_Half e_machine;
+ Elf64_Word e_version;
+ Elf64_Addr e_entry; /* Entry point */
+ Elf64_Off e_phoff;
+ Elf64_Off e_shoff;
+ Elf64_Word e_flags;
+ Elf64_Half e_ehsize;
+ Elf64_Half e_phentsize;
+ Elf64_Half e_phnum;
+ Elf64_Half e_shentsize;
+ Elf64_Half e_shnum;
+ Elf64_Half e_shstrndx;
+} Elf64_Ehdr;
+
+/* e_type */
+#define ET_EXEC 2
+#define ET_CORE 4
+
+/* sh_type */
+#define SHT_SYMTAB 2
+#define SHT_NOTE 7
+
+/* elf type */
+#define ELFDATANONE 0 /* e_ident[EI_DATA] */
+#define ELFDATA2LSB 1
+#define ELFDATA2MSB 2
+
+/* elf class */
+#define ELFCLASSNONE 0
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+
+/* magic number */
+#define EI_MAG0 0 /* e_ident[] indexes */
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+#define EI_PAD 7
+
+#define ELFMAG0 0x7f /* EI_MAG */
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+#define ELFMAG "\177ELF"
+
+typedef struct {
+ Elf32_Word p_type;
+ Elf32_Off p_offset;
+ Elf32_Addr p_vaddr;
+ Elf32_Addr p_paddr;
+ Elf32_Word p_filesz;
+ Elf32_Word p_memsz;
+ Elf32_Word p_flags;
+ Elf32_Word p_align;
+} Elf32_Phdr;
+
+#define PT_NULL 0 /* p_type */
+#define PT_LOAD 1
+#define PT_DYNAMIC 2
+#define PT_INTERP 3
+#define PT_NOTE 4
+#define PT_SHLIB 5
+#define PT_PHDR 6
+#define PT_NUM 7
+
+typedef struct {
+ Elf32_Word sh_name;
+ Elf32_Word sh_type;
+ Elf32_Word sh_flags;
+ Elf32_Addr sh_addr;
+ Elf32_Off sh_offset;
+ Elf32_Word sh_size;
+ Elf32_Word sh_link;
+ Elf32_Word sh_info;
+ Elf32_Word sh_addralign;
+ Elf32_Word sh_entsize;
+} Elf32_Shdr;
+
+typedef struct {
+ Elf64_Word sh_name;
+ Elf64_Word sh_type;
+ Elf64_Off sh_flags;
+ Elf64_Addr sh_addr;
+ Elf64_Off sh_offset;
+ Elf64_Off sh_size;
+ Elf64_Word sh_link;
+ Elf64_Word sh_info;
+ Elf64_Off sh_addralign;
+ Elf64_Off sh_entsize;
+} Elf64_Shdr;
+
+/* Notes used in ET_CORE */
+#define NT_PRSTATUS 1
+#define NT_PRFPREG 2
+#define NT_PRPSINFO 3
+#define NT_TASKSTRUCT 4
+
+/* Note header in a PT_NOTE section */
+typedef struct elf_note {
+ Elf32_Word n_namesz; /* Name size */
+ Elf32_Word n_descsz; /* Content size */
+ Elf32_Word n_type; /* Content type */
+} Elf32_Nhdr;
+
+typedef struct {
+ Elf64_Word n_namesz;
+ Elf64_Word n_descsz;
+ Elf64_Word n_type;
+} Elf64_Nhdr;
+
+#define NT_PRSTATUS 1
+#define NT_PRFPREG 2
+#define NT_PRPSINFO 3
+#define NT_PRXREG 4
+#define NT_PLATFORM 5
+#define NT_AUXV 6
+
+#endif
diff --git a/usr.bin/file/softmagic.c b/usr.bin/file/softmagic.c
new file mode 100644
index 0000000..5383eb8
--- /dev/null
+++ b/usr.bin/file/softmagic.c
@@ -0,0 +1,513 @@
+/*
+ * 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 <stdlib.h>
+#include <time.h>
+#include <sys/types.h>
+
+#include "file.h"
+
+#ifndef lint
+static char *moduleid =
+ "@(#)$Id: softmagic.c,v 1.1.1.3 1997/03/18 17:58:45 mpp 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 int32 mprint __P((union VALUETYPE *, struct magic *));
+static void mdebug __P((int32, 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;
+ static int32 *tmpoff = NULL;
+ static size_t tmplen = 0;
+ int32 oldoff = 0;
+
+ if (tmpoff == NULL)
+ if ((tmpoff = (int32 *) malloc(tmplen = 20)) == NULL)
+ error("out of memory\n");
+
+ 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;
+ }
+
+ tmpoff[cont_level] = 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 */
+ if (++cont_level >= tmplen)
+ if ((tmpoff = (int32 *) realloc(tmpoff,
+ tmplen += 20)) == NULL)
+ error("out of memory\n");
+ 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 (magic[magindex].flag & ADD) {
+ oldoff=magic[magindex].offset;
+ magic[magindex].offset += tmpoff[cont_level-1];
+ }
+ 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;
+ }
+ tmpoff[cont_level] = mprint(&p, &magic[magindex]);
+ if (magic[magindex].desc[0])
+ need_separator = 1;
+
+ /*
+ * If we see any continuations
+ * at a higher level,
+ * process them.
+ */
+ if (++cont_level >= tmplen)
+ if ((tmpoff =
+ (int32 *) realloc(tmpoff,
+ tmplen += 20)) == NULL)
+ error("out of memory\n");
+ }
+ if (magic[magindex].flag & ADD) {
+ magic[magindex].offset = oldoff;
+ }
+ }
+ }
+ return 1; /* all through */
+ }
+ return 0; /* no match at all */
+}
+
+static int32
+mprint(p, m)
+union VALUETYPE *p;
+struct magic *m;
+{
+ char *pp, *rt;
+ uint32 v;
+ int32 t=0 ;
+
+
+ switch (m->type) {
+ case BYTE:
+ v = p->b;
+ v = signextend(m, v) & m->mask;
+ (void) printf(m->desc, (unsigned char) v);
+ t = m->offset + sizeof(char);
+ break;
+
+ case SHORT:
+ case BESHORT:
+ case LESHORT:
+ v = p->h;
+ v = signextend(m, v) & m->mask;
+ (void) printf(m->desc, (unsigned short) v);
+ t = m->offset + sizeof(short);
+ break;
+
+ case LONG:
+ case BELONG:
+ case LELONG:
+ v = p->l;
+ v = signextend(m, v) & m->mask;
+ (void) printf(m->desc, (uint32) v);
+ t = m->offset + sizeof(int32);
+ break;
+
+ case STRING:
+ if (m->reln == '=') {
+ (void) printf(m->desc, m->value.s);
+ t = m->offset + strlen(m->value.s);
+ }
+ else {
+ if (*m->value.s == '\0') {
+ char *cp = strchr(p->s,'\n');
+ if (cp)
+ *cp = '\0';
+ }
+ (void) printf(m->desc, p->s);
+ t = m->offset + strlen(p->s);
+ }
+ break;
+
+ 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);
+ t = m->offset + sizeof(time_t);
+ break;
+
+ default:
+ error("invalid m->type (%d) in mprint().\n", m->type);
+ /*NOTREACHED*/
+ }
+ return(t);
+}
+
+/*
+ * Convert the byte order of the data we are looking at
+ */
+static int
+mconvert(p, m)
+union VALUETYPE *p;
+struct magic *m;
+{
+ switch (m->type) {
+ case BYTE:
+ case SHORT:
+ case LONG:
+ case DATE:
+ return 1;
+ case STRING:
+ {
+ char *ptr;
+
+ /* Null terminate and eat the return */
+ p->s[sizeof(p->s) - 1] = '\0';
+ if ((ptr = strchr(p->s, '\n')) != NULL)
+ *ptr = '\0';
+ return 1;
+ }
+ case BESHORT:
+ p->h = (short)((p->hs[0]<<8)|(p->hs[1]));
+ return 1;
+ case BELONG:
+ case BEDATE:
+ p->l = (int32)
+ ((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 = (int32)
+ ((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)
+int32 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;
+{
+ int32 offset = m->offset;
+
+ if (offset + sizeof(union VALUETYPE) <= nbytes)
+ memcpy(p, s + offset, sizeof(union VALUETYPE));
+ else {
+ /*
+ * the usefulness of padding with zeroes eludes me, it
+ * might even cause problems
+ */
+ int32 have = nbytes - offset;
+ memset(p, 0, sizeof(union VALUETYPE));
+ if (have > 0)
+ memcpy(p, s + offset, have);
+ }
+
+
+ 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 uint32 l = m->value.l;
+ register uint32 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, "%u == *any* = 1\n", v);
+ matched = 1;
+ break;
+
+ case '!':
+ matched = v != l;
+ if (debug)
+ (void) fprintf(stderr, "%u != %u = %d\n",
+ v, l, matched);
+ break;
+
+ case '=':
+ matched = v == l;
+ if (debug)
+ (void) fprintf(stderr, "%u == %u = %d\n",
+ v, l, matched);
+ break;
+
+ case '>':
+ if (m->flag & UNSIGNED) {
+ matched = v > l;
+ if (debug)
+ (void) fprintf(stderr, "%u > %u = %d\n",
+ v, l, matched);
+ }
+ else {
+ matched = (int32) v > (int32) l;
+ if (debug)
+ (void) fprintf(stderr, "%d > %d = %d\n",
+ v, l, matched);
+ }
+ break;
+
+ case '<':
+ if (m->flag & UNSIGNED) {
+ matched = v < l;
+ if (debug)
+ (void) fprintf(stderr, "%u < %u = %d\n",
+ v, l, matched);
+ }
+ else {
+ matched = (int32) v < (int32) l;
+ if (debug)
+ (void) fprintf(stderr, "%d < %d = %d\n",
+ v, l, matched);
+ }
+ break;
+
+ case '&':
+ matched = (v & l) == l;
+ if (debug)
+ (void) fprintf(stderr, "((%x & %x) == %x) = %d\n",
+ v, l, l, matched);
+ break;
+
+ case '^':
+ matched = (v & l) != l;
+ if (debug)
+ (void) fprintf(stderr, "((%x & %x) != %x) = %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..6a9e1dd
--- /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$ # 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..28cd21e
--- /dev/null
+++ b/usr.bin/file2c/Makefile
@@ -0,0 +1,6 @@
+# $Id$
+
+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..ef4e77b
--- /dev/null
+++ b/usr.bin/file2c/file2c.1
@@ -0,0 +1,46 @@
+.\"----------------------------------------------------------------------------
+.\" "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: file2c.1,v 1.4 1997/06/23 04:51:58 steve Exp $
+.\"
+.Dd January 28, 1995
+.Dt FILE2C 1
+.Os
+.Sh NAME
+.Nm file2c
+.Nd convert file to c-source
+.Sh SYNOPSIS
+.Nm
+.Op "string"
+.Op "string"
+.Sh DESCRIPTION
+The
+.Nm
+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..af1ea26
--- /dev/null
+++ b/usr.bin/find/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= find
+SRCS= find.c function.c ls.c main.c misc.c operator.c option.c
+CFLAGS+=-D_NEW_VFSCONF
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/find/extern.h b/usr.bin/find/extern.h
new file mode 100644
index 0000000..eb63ecd
--- /dev/null
+++ b/usr.bin/find/extern.h
@@ -0,0 +1,80 @@
+/*-
+ * 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_delete __P((void));
+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..652011e
--- /dev/null
+++ b/usr.bin/find/find.1
@@ -0,0 +1,489 @@
+.\" 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.7 (Berkeley) 5/9/95
+.\" $Id: find.1,v 1.9 1997/05/19 16:33:26 eivind Exp $
+.\"
+.Dd May 9, 1995
+.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.
+This is the default.
+.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 -delete
+Delete found files and/or directories. Always returns True. This executes
+from the current working directory as
+.Nm
+recurses down the tree. It will not attempt to delete a filename with a ``/''
+character in its pathname relative to "." for security reasons.
+Depth\-first traversal processing is implied by this option.
+.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 .
+The
+.Xr sysctl 8
+command can be used to find out the types of filesystems
+that are available on the system:
+.Bd -literal -offset indent
+sysctl vfs
+.Ed
+In addition, there are two pseudo-types, ``local'' and ``rdonly''.
+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 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.
+If none of
+.Ic -exec ,
+.Ic -ls ,
+.Ic -print0 ,
+or
+.Ic \&-ok
+is specified, the given expression shall be effectively replaced by
+.Cm \&( Ns Ar given\& expression Ns Cm \&)
+.Ic -print .
+.It Ic -print0
+This primary always evaluates to true.
+It prints the pathname of the current file to standard output, followed by an
+.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 ,
+.Ic -delete ,
+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.
+.Pp
+The
+.Ic -delete
+primary do not interact well with other options that cause the filesystem
+tree traversal options to be changed.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/usr.bin/find/find.c b/usr.bin/find/find.c
new file mode 100644
index 0000000..8afadf8b
--- /dev/null
+++ b/usr.bin/find/find.c
@@ -0,0 +1,202 @@
+/*-
+ * 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.5 (Berkeley) 8/5/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 bracket the current expression with parens, if
+ * necessary, and add a -print node on the end.
+ */
+ if (!isoutput) {
+ if (plan == NULL) {
+ new = c_print();
+ 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;
+#ifdef FTS_W
+ case FTS_W:
+ continue;
+#endif /* FTS_W */
+ }
+#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..ff007e7
--- /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, N_DELETE
+};
+
+/* 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..97d63c5
--- /dev/null
+++ b/usr.bin/find/function.c
@@ -0,0 +1,1141 @@
+/*-
+ * 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.10 (Berkeley) 5/4/95";
+#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 long
+find_parsenum(plan, option, vp, endch)
+ PLAN *plan;
+ char *option, *vp, *endch;
+{
+ long 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 strtoq(). Note, if strtoq() returns zero
+ * and endchar points to the beginning of the string we know we have
+ * a syntax error.
+ */
+ value = strtoq(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 int val_type, val_flags;
+ 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;
+
+ /*
+ * Further tests may need both of these values, so
+ * always copy both of them.
+ */
+ val_flags = sb.f_flags;
+ val_type = sb.f_type;
+ }
+ switch (plan->flags) {
+ case F_MTFLAG:
+ return (val_flags & plan->mt_data) != 0;
+ case F_MTTYPE:
+ return (val_type == plan->mt_data);
+ default:
+ abort();
+ }
+}
+
+PLAN *
+c_fstype(arg)
+ char *arg;
+{
+ register PLAN *new;
+ struct vfsconf vfc;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_FSTYPE, f_fstype);
+
+ /*
+ * Check first for a filesystem name.
+ */
+ if (getvfsbyname(arg, &vfc) == 0) {
+ new->flags = F_MTTYPE;
+ new->mt_data = vfc.vfc_typenum;
+ return (new);
+ }
+
+ switch (*arg) {
+ case 'l':
+ if (!strcmp(arg, "local")) {
+ new->flags = F_MTFLAG;
+ new->mt_data = MNT_LOCAL;
+ return (new);
+ }
+ break;
+ case 'r':
+ if (!strcmp(arg, "rdonly")) {
+ new->flags = F_MTFLAG;
+ new->mt_data = MNT_RDONLY;
+ 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, f or w
+ * for block special file, character special file, directory, FIFO,
+ * regular file or whiteout 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;
+#ifdef FTS_WHITEOUT
+ case 'w':
+ mask = S_IFWHT;
+ ftsoptions |= FTS_WHITEOUT;
+ break;
+#endif /* FTS_WHITEOUT */
+ default:
+ errx(1, "-type: %s: unknown type", typestring);
+ }
+
+ new = palloc(N_TYPE, f_type);
+ new->m_data = mask;
+ return (new);
+}
+
+/*
+ * -delete functions --
+ *
+ * True always. Makes it's best shot and continues on regardless.
+ */
+int
+f_delete(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ /* ignore these from fts */
+ if (strcmp(entry->fts_accpath, ".") == 0 ||
+ strcmp(entry->fts_accpath, "..") == 0)
+ return (1);
+
+ /* sanity check */
+ if (isdepth == 0 || /* depth off */
+ (ftsoptions & FTS_NOSTAT) || /* not stat()ing */
+ !(ftsoptions & FTS_PHYSICAL) || /* physical off */
+ (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */
+ errx(1, "-delete: insecure options got turned on");
+
+ /* Potentially unsafe - do not accept relative paths whatsoever */
+ if (strchr(entry->fts_accpath, '/') != NULL)
+ errx(1, "-delete: %s: relative path potentially not safe",
+ entry->fts_accpath);
+
+ /* Turn off user immutable bits if running as root */
+ if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
+ !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
+ geteuid() == 0)
+ chflags(entry->fts_accpath,
+ entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
+
+ /* rmdir directories, unlink everything else */
+ if (S_ISDIR(entry->fts_statp->st_mode)) {
+ if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY)
+ warn("-delete: rmdir(%s)", entry->fts_path);
+ } else {
+ if (unlink(entry->fts_accpath) < 0)
+ warn("-delete: unlink(%s)", entry->fts_path);
+ }
+
+ /* "succeed" */
+ return (1);
+}
+
+PLAN *
+c_delete()
+{
+
+ ftsoptions &= ~FTS_NOSTAT; /* no optimise */
+ ftsoptions |= FTS_PHYSICAL; /* disable -follow */
+ ftsoptions &= ~FTS_LOGICAL; /* disable -follow */
+ isoutput = 1; /* possible output */
+ isdepth = 1; /* -depth implied */
+
+ return (palloc(N_DELETE, f_delete));
+}
+
+/*
+ * -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..29c068d
--- /dev/null
+++ b/usr.bin/find/ls.c
@@ -0,0 +1,116 @@
+/*
+ * 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[80];
+
+ strftime(longstring, sizeof(longstring), "%c", localtime(&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..49ad226
--- /dev/null
+++ b/usr.bin/find/main.c
@@ -0,0 +1,156 @@
+/*-
+ * 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.4 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.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, ch;
+
+ (void)setlocale(LC_ALL, "");
+
+ (void)time(&now); /* initialize the time-of-day */
+
+ p = start = argv;
+ Hflag = Lflag = 0;
+ ftsoptions = FTS_NOSTAT | FTS_PHYSICAL;
+ while ((ch = getopt(argc, argv, "HLPXdf:x")) != -1)
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = 0;
+ break;
+ case 'P':
+ 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..22bfeb6
--- /dev/null
+++ b/usr.bin/find/operator.c
@@ -0,0 +1,276 @@
+/*-
+ * 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 we encounter ! ( expr ) then look for nots in
+ * the expr subplan.
+ */
+ if (node->type == N_EXPR)
+ node->p_data[0] = not_squish(node->p_data[0]);
+ 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 or'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..c316d4a
--- /dev/null
+++ b/usr.bin/find/option.c
@@ -0,0 +1,152 @@
+/*-
+ * 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 },
+ { "-delete", N_DELETE, c_delete, O_ZERO },
+ { "-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..e9abd72
--- /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.2 (Berkeley) 4/28/95
+ */
+
+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 *));
+PERSON *find_person __P((char *));
+int hide __P((struct passwd *));
+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..9d0755c
--- /dev/null
+++ b/usr.bin/finger/finger.1
@@ -0,0 +1,209 @@
+.\" Copyright (c) 1989, 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.
+.\"
+.\" @(#)finger.1 8.3 (Berkeley) 5/5/94
+.\"
+.Dd May 5, 1994
+.Dt FINGER 1
+.Os BSD 4
+.Sh NAME
+.Nm finger
+.Nd user information lookup program
+.Sh SYNOPSIS
+.Nm finger
+.Op Fl lmpshoT
+.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.
+.Pp
+.It Fl T
+Disable the use of T/TCP (see
+.Xr ttcp 4 ).
+This option is needed to finger hosts with a broken TCP implementation.
+.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 FILES
+.Bl -tag -width /var/log/lastlog -compact
+.It Pa /var/log/lastlog
+last login data base
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr w 1 ,
+.Xr who 1 ,
+.Xr ttcp 4 .
+.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..091ac16
--- /dev/null
+++ b/usr.bin/finger/finger.c
@@ -0,0 +1,335 @@
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)finger.c 8.5 (Berkeley) 5/4/95";
+#else
+static const char rcsid[] =
+ "$Id$";
+#endif
+#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 <db.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <db.h>
+#include <locale.h>
+
+#include "finger.h"
+
+DB *db;
+time_t now;
+int entries, lflag, mflag, pplan, sflag, oflag, Tflag;
+char tbuf[1024];
+
+static void loginlist __P((void));
+static void usage __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, "lmpshoT")) != -1)
+ 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 'T':
+ Tflag = 1; /* disable T/TCP */
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ return optind;
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: finger [-lmpshoT] [login ...]\n");
+ exit(1);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int envargc, argcnt;
+ char *envargv[3];
+
+ (void) setlocale(LC_ALL, "");
+
+ /* 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();
+ return (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(1, "%s", _PATH_UTMP);
+ name[UT_NAMESIZE] = '\0';
+ 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) {
+ PERSON *tmp;
+
+ r = (*db->seq)(db, &key, &data, sflag);
+ if (r == -1)
+ err(1, "db seq");
+ if (r == 1)
+ break;
+ memmove(&tmp, data.data, sizeof tmp);
+ enter_lastlog(tmp);
+ }
+}
+
+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(1, NULL);
+
+ /* 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)) != NULL) && !hide(pw))
+ enter_person(pw);
+ else
+ warnx("%s: no such user", *p);
+ else {
+ while ((pw = getpwent()) != NULL) {
+ 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)
+ warnx("%s: no such user", *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(1, "%s", _PATH_UTMP);
+ 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) {
+ PERSON *tmp;
+
+ r = (*db->seq)(db, &key, &data, sflag);
+ if (r == -1)
+ err(1, "db seq");
+ if (r == 1)
+ break;
+ memmove(&tmp, data.data, sizeof tmp);
+ enter_lastlog(tmp);
+ }
+}
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..a0643ca
--- /dev/null
+++ b/usr.bin/finger/lprint.c
@@ -0,0 +1,362 @@
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)lprint.c 8.3 (Berkeley) 4/28/95";
+#else
+static const char rcsid[] =
+ "$Id$";
+#endif
+#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 <err.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;
+ PERSON *tmp;
+ DBT data, key;
+
+ for (sflag = R_FIRST;; sflag = R_NEXT) {
+ r = (*db->seq)(db, &key, &data, sflag);
+ if (r == -1)
+ err(1, "db seq");
+ if (r == 1)
+ break;
+ memmove(&tmp, data.data, sizeof tmp);
+ pn = tmp;
+ 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)
+ if (*p != '\r')
+ 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)
+ if (ch != '\r')
+ 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..e4bbfc6
--- /dev/null
+++ b/usr.bin/finger/net.c
@@ -0,0 +1,185 @@
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)net.c 8.4 (Berkeley) 4/28/95";
+#else
+static const char rcsid[] =
+ "$Id$";
+#endif
+#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 <err.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <utmp.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/uio.h>
+#include "finger.h"
+
+void
+netfinger(name)
+ char *name;
+{
+ extern int lflag;
+ extern int Tflag;
+ 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;
+ struct iovec iov[3];
+ struct msghdr msg;
+
+ if (!(host = rindex(name, '@')))
+ return;
+ *host++ = '\0';
+ 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))) {
+ warnx("unknown host: %s", host);
+ return;
+ }
+ if (!(sp = getservbyname("finger", "tcp"))) {
+ warnx("tcp/finger: unknown service");
+ 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);
+
+ msg.msg_name = (void *)&sin;
+ msg.msg_namelen = sizeof sin;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 0;
+ msg.msg_control = 0;
+ msg.msg_controllen = 0;
+ msg.msg_flags = MSG_EOF;
+
+ /* -l flag for remote fingerd */
+ if (lflag) {
+ iov[msg.msg_iovlen].iov_base = "/W ";
+ iov[msg.msg_iovlen++].iov_len = 3;
+ }
+ /* send the name followed by <CR><LF> */
+ iov[msg.msg_iovlen].iov_base = name;
+ iov[msg.msg_iovlen++].iov_len = strlen(name);
+ iov[msg.msg_iovlen].iov_base = "\r\n";
+ iov[msg.msg_iovlen++].iov_len = 2;
+
+ /* -T disables T/TCP: compatibility option to finger broken hosts */
+ if (Tflag && connect(s, (struct sockaddr *)&sin, sizeof (sin))) {
+ perror("finger: connect");
+ return;
+ }
+
+ if (sendmsg(s, &msg, MSG_EOF) < 0) {
+ perror("finger: sendmsg");
+ close(s);
+ return;
+ }
+
+ /*
+ * 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.
+ */
+ lastc = 0;
+ if ((fp = fdopen(s, "r")) != NULL) {
+ while ((c = getc(fp)) != EOF) {
+ if (c == 0x0d) {
+ if (lastc == '\r') /* ^M^M - skip dupes */
+ continue;
+ c = '\n';
+ lastc = '\r';
+ } else {
+ if (!isprint(c) && !isspace(c)) {
+ c &= 0x7f;
+ c |= 0x40;
+ }
+ if (lastc != '\r' || c != '\n')
+ lastc = c;
+ else {
+ lastc = '\n';
+ continue;
+ }
+ }
+ putchar(c);
+ }
+ if (lastc != '\n')
+ putchar('\n');
+
+ if (ferror(fp)) {
+ /*
+ * Assume that whatever it was set errno...
+ */
+ perror("finger: read");
+ }
+ (void)fclose(fp);
+ }
+}
diff --git a/usr.bin/finger/sprint.c b/usr.bin/finger/sprint.c
new file mode 100644
index 0000000..8c38d7c
--- /dev/null
+++ b/usr.bin/finger/sprint.c
@@ -0,0 +1,174 @@
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)sprint.c 8.3 (Berkeley) 4/28/95";
+#else
+static const char rcsid[] =
+ "$Id$";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+#include <db.h>
+#include <err.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];
+ PERSON *tmp;
+ 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 17 /* 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 Phone" : " Where");
+
+ for (sflag = R_FIRST;; sflag = R_NEXT) {
+ r = (*db->seq)(db, &key, &data, sflag);
+ if (r == -1)
+ err(1, "db seq");
+ if (r == 1)
+ break;
+ memmove(&tmp, data.data, sizeof tmp);
+ pn = tmp;
+
+ 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(" %-7.7s", pn->office);
+ else if (pn->officephone)
+ (void)printf(" %-7.7s", " ");
+ if (pn->officephone)
+ (void)printf(" %-.9s",
+ 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..0a48d46
--- /dev/null
+++ b/usr.bin/finger/util.c
@@ -0,0 +1,405 @@
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)util.c 8.3 (Berkeley) 4/28/95";
+#else
+static const char rcsid[] =
+ "$Id$";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <db.h>
+#include <err.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) != '\0'; ++p)
+ if (*t == '&') {
+ (void)strcpy(t, pw->pw_name);
+ while (*++t);
+ }
+ else
+ ++t;
+ for (t = name; (p = strtok(t, "\t ")) != NULL; 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] = '\0';
+ 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(1, NULL);
+
+ key.data = pw->pw_name;
+ key.size = strlen(pw->pw_name);
+
+ switch ((*db->get)(db, &key, &data, 0)) {
+ case 0:
+ memmove(&pn, data.data, sizeof pn);
+ return (pn);
+ default:
+ case -1:
+ err(1, "db get");
+ /* 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(1, "db put");
+ return (pn);
+ }
+}
+
+PERSON *
+find_person(name)
+ char *name;
+{
+ struct passwd *pw;
+
+ register int cnt;
+ DBT data, key;
+ PERSON *p;
+ 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;
+
+ if ((*db->get)(db, &key, &data, 0))
+ return (NULL);
+ memmove(&p, data.data, sizeof p);
+ return (p);
+}
+
+PERSON *
+palloc()
+{
+ PERSON *p;
+
+ if ((p = malloc((u_int) sizeof(PERSON))) == NULL)
+ err(1, NULL);
+ return(p);
+}
+
+static WHERE *
+walloc(pn)
+ register PERSON *pn;
+{
+ register WHERE *w;
+
+ if ((w = malloc((u_int) sizeof(WHERE))) == NULL)
+ err(1, NULL);
+ 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) {
+ warn(tbuf);
+ 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) != '\0'; ++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) {
+ warn("%s", tbuf);
+ return;
+ }
+ } else if (sb.st_size != 0) {
+ pn->mailrecv = sb.st_mtime;
+ pn->mailread = sb.st_atime;
+ }
+}
+
+/*
+ * Is this user hiding from finger?
+ * If ~<user>/.nofinger exists, return 1 (hide), else return 0 (nohide).
+ */
+
+int
+hide(pw)
+ struct passwd *pw;
+{
+ 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..7581055
--- /dev/null
+++ b/usr.bin/fmt/fmt.1
@@ -0,0 +1,95 @@
+.\" 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
+.Fl c
+.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
+.Fl c
+instructs
+.Nm
+to center the text.
+.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 mail 1 ,
+.Xr nroff 1
+.Sh HISTORY
+The
+.Nm
+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..3c85550
--- /dev/null
+++ b/usr.bin/fmt/fmt.c
@@ -0,0 +1,550 @@
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)fmt.c 8.1 (Berkeley) 7/20/93";
+#else
+static const char rcsid[] =
+ "$Id$";
+#endif
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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 */
+int center;
+
+char *headnames[] = {"To", "Subject", "Cc", 0};
+
+void fmt __P((FILE *));
+int ispref __P((char *, char *));
+void leadin __P((void));
+void oflush __P((void));
+void pack __P((char [], int));
+void prefix __P((char []));
+void setout __P((void));
+void split __P((char []));
+void tabulate __P((char []));
+
+/*
+ * Drive the whole formatter by managing input files. Also,
+ * cause initialization of the output stuff and flush it out
+ * at the end.
+ */
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register FILE *fi;
+ register int errs = 0;
+ int number; /* LIZ@UOM 6/18/85 */
+
+ (void) setlocale(LC_CTYPE, "");
+
+ 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 && !strcmp(argv[1], "-c")) {
+ center++;
+ argc--;
+ argv++;
+ }
+ 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)
+ errx(1, "max length must be greater than goal length");
+ 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.
+ */
+void
+fmt(fi)
+ FILE *fi;
+{
+ static char *linebuf = 0, *canonb = 0;
+ register char *cp, *cp2, cc;
+ register int c, col;
+#define CHUNKSIZE 1024
+ static int lbufsize = 0, cbufsize = 0;
+
+ if (center) {
+ linebuf = malloc(BUFSIZ);
+ while (1) {
+ cp = fgets(linebuf, BUFSIZ, fi);
+ if (!cp)
+ return;
+ while (*cp && isspace(*cp))
+ cp++;
+ cp2 = cp + strlen(cp) - 1;
+ while (cp2 > cp && isspace(*cp2))
+ cp2--;
+ if (cp == cp2)
+ putchar('\n');
+ col = cp2 - cp;
+ for (c = 0; c < (goal_length-col)/2; c++)
+ putchar(' ');
+ while (cp <= cp2)
+ putchar(*cp++);
+ putchar('\n');
+ }
+ }
+ c = getc(fi);
+ while (c != EOF) {
+ /*
+ * Collect a line, doing ^H processing.
+ * Leave tabs for now.
+ */
+ cp = linebuf;
+ while (c != '\n' && c != EOF) {
+ if (cp - linebuf >= lbufsize) {
+ int offset = cp - linebuf;
+ lbufsize += CHUNKSIZE;
+ linebuf = realloc(linebuf, lbufsize);
+ if(linebuf == 0)
+ abort();
+ cp = linebuf + offset;
+ }
+ 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);
+ }
+
+ /*
+ * Toss anything remaining on the input line.
+ */
+ while (c != '\n' && c != EOF)
+ c = getc(fi);
+
+ if (cp != NULL) {
+ *cp = '\0';
+ } else {
+ putchar('\n');
+ c = getc(fi);
+ continue;
+ }
+
+ /*
+ * Expand tabs on the way to canonb.
+ */
+ col = 0;
+ cp = linebuf;
+ cp2 = canonb;
+ while ((cc = *cp++)) {
+ if (cc != '\t') {
+ col++;
+ if (cp2 - canonb >= cbufsize) {
+ int offset = cp2 - canonb;
+ cbufsize += CHUNKSIZE;
+ canonb = realloc(canonb, cbufsize);
+ if(canonb == 0)
+ abort();
+ cp2 = canonb + offset;
+ }
+ *cp2++ = cc;
+ continue;
+ }
+ do {
+ if (cp2 - canonb >= cbufsize) {
+ int offset = cp2 - canonb;
+ cbufsize += CHUNKSIZE;
+ canonb = realloc(canonb, cbufsize);
+ if(canonb == 0)
+ abort();
+ cp2 = canonb + offset;
+ }
+ *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.
+ */
+void
+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.
+ */
+void
+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.
+ */
+void
+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[];
+ */
+void
+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.
+ */
+void
+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).
+ */
+void
+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.
+ */
+void
+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)
+ errx(1, "ran out of memory");
+ strcpy(top, str);
+ return (top);
+}
+
+/*
+ * Is s1 a prefix of s2??
+ */
+int
+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..dd5b541
--- /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:")) != -1)
+ 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..780eba8
--- /dev/null
+++ b/usr.bin/from/from.1
@@ -0,0 +1,91 @@
+.\" 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.
+Read from standard input if file name
+.Ar -
+is given.
+.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 ENVIRONMENT
+.Bl -tag -width Fl
+.It Ev MAIL
+If set, the location of the invoker's mailbox. Otherwise, the default
+in /var/mail is used.
+.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..8d20ad5
--- /dev/null
+++ b/usr.bin/from/from.c
@@ -0,0 +1,149 @@
+/*
+ * 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 <stdlib.h>
+#include <string.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:?")) != -1)
+ 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 (*argv) {
+ (void)sprintf(buf, "%s/%s", _PATH_MAILDIR, *argv);
+ file = buf;
+ } else {
+ if (!(file = getenv("MAIL"))) {
+ if (!(pwd = getpwuid(getuid()))) {
+ (void)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;
+ }
+ }
+ }
+
+ /* read from stdin */
+ if (strcmp(file, "-") == 0) {
+ }
+ else 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..d3f7c2d
--- /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 executable 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 - executable 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 1 ) ,
+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 iostat 8 ,
+.Xr pstat 8 ,
+.Xr vmstat 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..4ffab31
--- /dev/null
+++ b/usr.bin/fstat/fstat.c
@@ -0,0 +1,800 @@
+/*-
+ * 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.3 (Berkeley) 5/2/95";
+#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>
+#include <sys/pipe.h>
+#define KERNEL
+#include <sys/file.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#undef KERNEL
+#include <sys/mount.h>
+#include <nfs/nfsproto.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsnode.h>
+
+#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 <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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;
+
+void dofiles __P((struct kinfo_proc *kp));
+void vtrans __P((struct vnode *vp, int i, int flag));
+int ufs_filestat __P((struct vnode *vp, struct filestat *fsp));
+int nfs_filestat __P((struct vnode *vp, struct filestat *fsp));
+char *getmnton __P((struct mount *m));
+void pipetrans __P((struct pipe *pi, int i, int flag));
+void socktrans __P((struct socket *sock, int i));
+void getinetproto __P((int number));
+int getfname __P((char *filename));
+void usage __P((void));
+
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register struct passwd *passwd;
+ struct kinfo_proc *p, *plast;
+ int arg, ch, what;
+ char *memf, *nlistf;
+ char buf[_POSIX2_LINE_MAX];
+ int cnt;
+
+ arg = 0;
+ what = KERN_PROC_ALL;
+ nlistf = memf = NULL;
+ while ((ch = getopt(argc, argv, "fnp:u:vNM")) != -1)
+ 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_openfiles(nlistf, memf, NULL, O_RDONLY, buf)) == NULL) {
+ fprintf(stderr, "fstat: %s\n", buf);
+ 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;
+
+ 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);
+ /*
+ * text vnode, if one
+ */
+ if (p->p_textvp)
+ vtrans(p->p_textvp, TEXT, FREAD);
+ /*
+ * 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);
+ }
+#ifdef DTYPE_PIPE
+ else if (file.f_type == DTYPE_PIPE) {
+ if (checkfile == 0)
+ pipetrans((struct pipe *)file.f_data, i,
+ file.f_flag);
+ }
+#endif
+ 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
+pipetrans(pi, i, flag)
+ struct pipe *pi;
+ int i;
+ int flag;
+{
+ struct pipe pip;
+ char rw[3];
+
+ PREFIX(i);
+
+ /* fill in socket */
+ if (!KVM_READ(pi, &pip, sizeof(struct pipe))) {
+ dprintf(stderr, "can't read pipe at %x\n", pi);
+ goto bad;
+ }
+
+ printf("* pipe %8x <-> %8x", (int)pi, (int)pip.pipe_peer);
+ printf(" %6d", (int)pip.pipe_buffer.cnt);
+ rw[0] = '\0';
+ if (flag & FREAD)
+ strcat(rw, "r");
+ if (flag & FWRITE)
+ strcat(rw, "w");
+ printf(" %2s", rw);
+ putchar('\n');
+ return;
+
+bad:
+ printf("* error\n");
+}
+
+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);
+}
+
+int
+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..4bb9dc0
--- /dev/null
+++ b/usr.bin/ftp/Makefile
@@ -0,0 +1,15 @@
+# $Id$
+# $NetBSD: Makefile,v 1.11 1997/03/24 21:59:36 christos Exp $
+# from: @(#)Makefile 8.2 (Berkeley) 4/3/94
+
+PROG= ftp
+SRCS= cmds.c cmdtab.c complete.c domacro.c fetch.c ftp.c main.c ruserpass.c \
+ util.c
+
+LDADD+= -ledit -ltermcap
+DPADD+= ${LIBEDIT} ${LIBTERMCAP}
+
+LINKS= ${BINDIR}/ftp ${BINDIR}/pftp
+MLINKS= ftp.1 pftp.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ftp/cmds.c b/usr.bin/ftp/cmds.c
new file mode 100644
index 0000000..2232553
--- /dev/null
+++ b/usr.bin/ftp/cmds.c
@@ -0,0 +1,2114 @@
+/* $Id: cmds.c,v 1.8 1997/06/27 09:30:01 ache Exp $ */
+/* $NetBSD: cmds.c,v 1.24 1997/05/17 19:44:36 pk Exp $ */
+
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94";
+#else
+static char rcsid[] = "$Id: cmds.c,v 1.8 1997/06/27 09:30:01 ache Exp $";
+#endif
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Command Routines.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <arpa/ftp.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <glob.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ftp_var.h"
+#include "pathnames.h"
+
+jmp_buf jabort;
+char *mname;
+char *home = "/";
+
+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 = " | ";
+ }
+ puts(" ]");
+ 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) {
+ warnx("internal error: unknown type %d.", 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")) || argc > 3) {
+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)) != NULL) {
+ if (*cp == '\0') {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ tp = cp;
+ if (mcase) {
+ while (*tp && !islower((unsigned char)*tp)) {
+ tp++;
+ }
+ if (!*tp) {
+ tp = cp;
+ tp2 = tmpbuf;
+ while ((*tp2 = *tp) != '\0') {
+ if (isupper((unsigned char)*tp2))
+ *tp2 = tolower((unsigned char)*tp2);
+ 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;
+ 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[];
+ int restartit;
+ const char *mode;
+{
+ 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")) || argc > 3) {
+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((unsigned char)*tp)) {
+ tp++;
+ }
+ if (!*tp) {
+ tp = argv[2];
+ tp2 = tmpbuf;
+ while ((*tp2 = *tp) != '\0') {
+ if (isupper((unsigned char)*tp2))
+ *tp2 = tolower((unsigned char)*tp2);
+ 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) {
+ time_t mtime;
+
+ mtime = remotemodtime(argv[1], 0);
+ if (mtime == -1)
+ return (0);
+ if (stbuf.st_mtime >= mtime)
+ return (1);
+ }
+ }
+ }
+
+ 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, oconf;
+
+ alarmtimer(0);
+ putchar('\n');
+ (void)fflush(stdout);
+ if (mflag && fromatty) {
+ ointer = interactive;
+ oconf = confirmrest;
+ interactive = 1;
+ confirmrest = 0;
+ if (confirm("Continue with", mname)) {
+ interactive = ointer;
+ confirmrest = oconf;
+ longjmp(jabort, 0);
+ }
+ interactive = ointer;
+ confirmrest = oconf;
+ }
+ 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)) != NULL) {
+ if (*cp == '\0') {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ tp = cp;
+ if (mcase) {
+ for (tp2 = tmpbuf; (ch = (unsigned char)*tp++) != 0; )
+ *tp2++ = isupper(ch) ? tolower(ch) : ch;
+ *tp2 = '\0';
+ 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 *
+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 %sto %s.\n",
+ connected == -1 ? "and logged in" : "", hostname);
+ else
+ puts("Not connected.");
+ if (!proxy) {
+ pswitch(1);
+ if (connected) {
+ printf("Connected for proxy commands to %s.\n",
+ hostname);
+ }
+ else {
+ puts("No proxy connection.");
+ }
+ pswitch(0);
+ }
+ printf("Passive mode: %s.\n", onoff(passivemode));
+ 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("Preserve modification times: %s.\n", onoff(preserve));
+ printf("Case: %s; CR stripping: %s.\n", onoff(mcase), onoff(crflag));
+ if (ntflag) {
+ printf("Ntrans: (in) %s (out) %s\n", ntin, ntout);
+ }
+ else {
+ puts("Ntrans: off.");
+ }
+ if (mapflag) {
+ printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
+ }
+ else {
+ puts("Nmap: off.");
+ }
+ printf("Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
+ onoff(hash), mark, onoff(progress));
+ printf("Use of PORT cmds: %s.\n", onoff(sendport));
+#ifndef SMALL
+ printf("Command line editing: %s.\n", onoff(editing));
+#endif /* !SMALL */
+ if (macnum > 0) {
+ puts("Macros:");
+ for (i=0; i<macnum; i++) {
+ printf("\t%s\n", macros[i].mac_name);
+ }
+ }
+ code = 0;
+}
+
+/*
+ * Toggle a variable
+ */
+int
+togglevar(argc, argv, var, mesg)
+ int argc;
+ char *argv[];
+ int *var;
+ const char *mesg;
+{
+ if (argc < 2) {
+ *var = !*var;
+ } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
+ *var = 1;
+ } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
+ *var = 0;
+ } else {
+ printf("usage: %s [ on | off ]\n", argv[0]);
+ return (-1);
+ }
+ if (mesg)
+ printf("%s %s.\n", mesg, onoff(*var));
+ return (*var);
+}
+
+/*
+ * Set beep on cmd completed mode.
+ */
+/*VARARGS*/
+void
+setbell(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &bell, "Bell mode");
+}
+
+#ifndef SMALL
+/*
+ * Set command line editing
+ */
+/*VARARGS*/
+void
+setedit(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &editing, "Editing mode");
+ controlediting();
+}
+#endif /* !SMALL */
+
+/*
+ * Turn on packet tracing.
+ */
+/*VARARGS*/
+void
+settrace(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &trace, "Packet tracing");
+}
+
+/*
+ * Toggle hash mark printing during transfers, or set hash mark bytecount.
+ */
+/*VARARGS*/
+void
+sethash(argc, argv)
+ int argc;
+ char *argv[];
+{
+ if (argc == 1)
+ hash = !hash;
+ else if (argc != 2) {
+ printf("usage: %s [ on | off | bytecount ]\n", argv[0]);
+ code = -1;
+ return;
+ } else if (strcasecmp(argv[1], "on") == 0)
+ hash = 1;
+ else if (strcasecmp(argv[1], "off") == 0)
+ hash = 0;
+ else {
+ int nmark = atol(argv[1]);
+ if (nmark < 1) {
+ printf("%s: bad bytecount value.\n", argv[1]);
+ code = -1;
+ return;
+ }
+ mark = nmark;
+ hash = 1;
+ }
+ printf("Hash mark printing %s", onoff(hash));
+ if (hash)
+ printf(" (%d bytes/hash mark)", mark);
+ puts(".");
+ code = hash;
+}
+
+/*
+ * Turn on printing of server echo's.
+ */
+/*VARARGS*/
+void
+setverbose(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &verbose, "Verbose mode");
+}
+
+/*
+ * Toggle PORT cmd use before each data connection.
+ */
+/*VARARGS*/
+void
+setport(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &sendport, "Use of PORT cmds");
+}
+
+/*
+ * Toggle transfer progress bar.
+ */
+/*VARARGS*/
+void
+setprogress(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &progress, "Progress bar");
+}
+
+/*
+ * Turn on interactive prompting
+ * during mget, mput, and mdelete.
+ */
+/*VARARGS*/
+void
+setprompt(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &interactive, "Interactive mode");
+}
+
+/*
+ * Toggle metacharacter interpretation
+ * on local file names.
+ */
+/*VARARGS*/
+void
+setglob(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &doglob, "Globbing");
+}
+
+/*
+ * Toggle preserving modification times on retreived files.
+ */
+/*VARARGS*/
+void
+setpreserve(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &preserve, "Preserve modification times");
+}
+
+/*
+ * Set debugging mode on/off and/or
+ * set level of debugging.
+ */
+/*VARARGS*/
+void
+setdebug(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int val;
+
+ if (argc > 2) {
+ printf("usage: %s [ on | off | debuglevel ]\n", argv[0]);
+ code = -1;
+ return;
+ } else if (argc == 2) {
+ if (strcasecmp(argv[1], "on") == 0)
+ debug = 1;
+ else if (strcasecmp(argv[1], "off") == 0)
+ debug = 0;
+ else {
+ val = atoi(argv[1]);
+ if (val < 0) {
+ printf("%s: bad debugging value.\n", argv[1]);
+ code = -1;
+ return;
+ }
+ debug = val;
+ }
+ } else
+ debug = !debug;
+ 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[];
+{
+ int r;
+
+ if ((argc < 2 && !another(&argc, &argv, "remote-directory")) ||
+ argc > 2) {
+ printf("usage: %s remote-directory\n", argv[0]);
+ code = -1;
+ return;
+ }
+ r = command("CWD %s", argv[1]);
+ if (r == ERROR && code == 500) {
+ if (verbose)
+ puts("CWD command not recognized, trying XCWD.");
+ r = command("XCWD %s", argv[1]);
+ }
+ if (r == COMPLETE)
+ dirchange = 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 (getcwd(buf, sizeof(buf)) != NULL)
+ printf("Local directory now %s\n", buf);
+ else
+ warn("getcwd: %s", argv[1]);
+ code = 0;
+}
+
+/*
+ * Delete a single file.
+ */
+void
+delete(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) {
+ 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)) != 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")) || argc > 3) {
+usage:
+ printf("usage: %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[];
+{
+ const 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 = strcmp(argv[0], "dir") == 0 ? "LIST" : "NLST";
+ 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);
+
+ /* flush results in case commands are coming from a pipe */
+ fflush(stdout);
+}
+
+/*
+ * Get a directory listing
+ * of multiple remote files.
+ */
+void
+mls(argc, argv)
+ int argc;
+ char *argv[];
+{
+ sig_t oldintr;
+ int ointer, i;
+ const char *cmd;
+ char 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 = strcmp(argv[0], "mls") == 0 ? "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[MAXPATHLEN], *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;
+ shellnam[0] = '-';
+ (void)strncpy(shellnam + 1, ++namep, sizeof(shellnam) - 2);
+ shellnam[sizeof(shellnam) - 1] = '\0';
+ if (strcmp(namep, "sh") != 0)
+ shellnam[0] = '+';
+ if (debug) {
+ puts(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("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) {
+ (void)fputs("Account: ", stdout);
+ (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) {
+ puts("Login failed.");
+ return;
+ }
+ if (!aflag && argc == 4) {
+ (void)command("ACCT %s", argv[3]);
+ }
+ connected = -1;
+}
+
+/*
+ * Print working directory on remote machine.
+ */
+/*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) {
+ puts("PWD command not recognized, trying XPWD.");
+ (void)command("XPWD");
+ }
+ verbose = oldverbose;
+}
+
+/*
+ * Print working directory on local machine.
+ */
+void
+lpwd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char buf[MAXPATHLEN];
+
+ if (getcwd(buf, sizeof(buf)) != NULL)
+ printf("Local directory %s\n", buf);
+ else
+ warn("getcwd");
+ code = 0;
+}
+
+/*
+ * Make a directory.
+ */
+void
+makedir(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
+ argc > 2) {
+ printf("usage: %s directory-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("MKD %s", argv[1]) == ERROR && code == 500) {
+ if (verbose)
+ puts("MKD command not recognized, trying XMKD.");
+ (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")) ||
+ argc > 2) {
+ printf("usage: %s directory-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("RMD %s", argv[1]) == ERROR && code == 500) {
+ if (verbose)
+ puts("RMD command not recognized, trying XRMD.");
+ (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)
+ const char *initial;
+ int argc;
+ char *argv[];
+{
+ int i, len;
+ char buf[BUFSIZ]; /* must be >= sizeof(line) */
+
+ (void)strncpy(buf, initial, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ if (argc > 1) {
+ len = strlen(buf);
+ len += strlen(strncpy(&buf[len], argv[1],
+ sizeof(buf) - len - 1));
+ for (i = 2; i < argc && len < sizeof(buf); i++) {
+ buf[len++] = ' ';
+ len += strlen(strncpy(&buf[len], argv[i],
+ sizeof(buf) - len) - 1);
+ }
+ }
+ 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")) || argc > 3) {
+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;
+ }
+}
+
+void
+account(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *ap;
+
+ if (argc > 2) {
+ printf("usage: %s [password]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ else if (argc == 2)
+ ap = argv[1];
+ else
+ ap = getpass("Account:");
+ (void)command("ACCT %s", ap);
+}
+
+jmp_buf abortprox;
+
+void
+proxabort(notused)
+ int notused;
+{
+
+ alarmtimer(0);
+ 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;
+ int cmdpos;
+ 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) {
+ puts("?Ambiguous command.");
+ (void)fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (c == 0) {
+ puts("?Invalid command.");
+ (void)fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (!c->c_proxy) {
+ puts("?Invalid proxy command.");
+ (void)fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (setjmp(abortprox)) {
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, proxabort);
+ pswitch(1);
+ if (c->c_conn && !connected) {
+ puts("Not connected.");
+ (void)fflush(stdout);
+ pswitch(0);
+ (void)signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ cmdpos = strcspn(line, " \t");
+ if (cmdpos > 0) /* remove leading "proxy " from input buffer */
+ memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
+ (*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[];
+{
+
+ code = togglevar(argc, argv, &mcase, "Case mapping");
+}
+
+void
+setcr(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
+}
+
+void
+setntrans(argc, argv)
+ int argc;
+ char *argv[];
+{
+ if (argc == 1) {
+ ntflag = 0;
+ puts("Ntrans off.");
+ code = ntflag;
+ return;
+ }
+ ntflag++;
+ code = ntflag;
+ (void)strncpy(ntin, argv[1], sizeof(ntin) - 1);
+ ntin[sizeof(ntin) - 1] = '\0';
+ if (argc == 2) {
+ ntout[0] = '\0';
+ return;
+ }
+ (void)strncpy(ntout, argv[2], sizeof(ntout) - 1);
+ ntout[sizeof(ntout) - 1] = '\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;
+ puts("Nmap off.");
+ code = mapflag;
+ return;
+ }
+ if ((argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
+ 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((unsigned char)*(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((unsigned char)*(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) {
+ puts(
+"nmap: unbalanced brackets.");
+ return (name);
+ }
+ match = 1;
+ cp2--;
+ }
+ if (match) {
+ while (*++cp2 && *cp2 != ']') {
+ if (*cp2 == '\\' && *(cp2 + 1)) {
+ cp2++;
+ }
+ }
+ if (!*cp2) {
+ puts(
+"nmap: unbalanced brackets.");
+ return (name);
+ }
+ break;
+ }
+ switch (*++cp2) {
+ case ',':
+ goto LOOP;
+ case ']':
+ break;
+ default:
+ cp2--;
+ goto LOOP;
+ }
+ break;
+ case '$':
+ if (isdigit((unsigned char)*(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
+setpassive(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &passivemode,
+ verbose ? "Passive mode" : NULL);
+}
+
+void
+setsunique(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &sunique, "Store unique");
+}
+
+void
+setrunique(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ code = togglevar(argc, argv, &runique, "Receive unique");
+}
+
+/* change directory to parent directory */
+void
+cdup(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int r;
+
+ r = command("CDUP");
+ if (r == ERROR && code == 500) {
+ if (verbose)
+ puts("CDUP command not recognized, trying XCUP.");
+ r = command("XCUP");
+ }
+ if (r == COMPLETE)
+ dirchange = 1;
+}
+
+/* restart transfer at specific point */
+void
+restart(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc != 2)
+ puts("restart: offset not specified.");
+ else {
+ restart_point = atol(argv[1]);
+ printf("Restarting at %qd. Execute get, put or append to"
+ "initiate transfer\n", restart_point);
+ }
+}
+
+/* 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) {
+ puts("Limit of 16 macros have already been defined.");
+ code = -1;
+ return;
+ }
+ if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
+ printf("usage: %s macro_name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (interactive)
+ puts(
+"Enter macro line by line, terminating it with a null line.");
+ (void)strncpy(macros[macnum].mac_name, argv[1],
+ sizeof(macros[macnum].mac_name) - 1);
+ macros[macnum].mac_name[sizeof(macros[macnum].mac_name) - 1] = '\0';
+ 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) {
+ puts("macdef: end of file encountered.");
+ 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') {
+ puts("Macro not defined - 4K buffer exceeded.");
+ code = -1;
+ return;
+ }
+ }
+}
+
+/*
+ * Restrict FTP data port range to a high group of "safe" ports
+ */
+void
+setrestrict(argc, argv)
+ int argc;
+ char *argv[];
+{
+ code = togglevar(argc, argv, &restricted_data_ports,
+ verbose ? "Restricted data ports" : NULL);
+}
+
+/*
+ * get size of file on remote machine
+ */
+void
+sizecmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ off_t size;
+
+ if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
+ printf("usage: %s filename\n", argv[0]);
+ code = -1;
+ return;
+ }
+ size = remotesize(argv[1], 1);
+ if (size != -1)
+ printf("%s\t%qd\n", argv[1], size);
+ code = size;
+}
+
+/*
+ * get last modification time of file on remote machine
+ */
+void
+modtime(argc, argv)
+ int argc;
+ char *argv[];
+{
+ time_t mtime;
+
+ if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
+ printf("usage: %s filename\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mtime = remotemodtime(argv[1], 1);
+ if (mtime != -1)
+ printf("%s\t%s", argv[1], asctime(localtime(&mtime)));
+ code = mtime;
+}
+
+/*
+ * show status on remote 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]);
+}
+
+/*
+ * Display one file through $PAGER (defaults to "more").
+ */
+void
+page(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int orestart_point, ohash, overbose;
+ char *p, *pager;
+
+ if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
+ printf("usage: %s filename\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (!globulize(&argv[1])) {
+ code = -1;
+ return;
+ }
+ p = getenv("PAGER");
+ if (p == NULL)
+ p = PAGER;
+ if ((pager = malloc(strlen(p) + 2)) == NULL)
+ errx(1, "Can't allocate memory for $PAGER");
+ (void)sprintf(pager, "|%s", p);
+
+ orestart_point = restart_point;
+ ohash = hash;
+ overbose = verbose;
+ restart_point = hash = verbose = 0;
+ recvrequest("RETR", pager, argv[1], "r+w", 1);
+ (void)free(pager);
+ restart_point = orestart_point;
+ hash = ohash;
+ verbose = overbose;
+}
diff --git a/usr.bin/ftp/cmdtab.c b/usr.bin/ftp/cmdtab.c
new file mode 100644
index 0000000..f67eeb0
--- /dev/null
+++ b/usr.bin/ftp/cmdtab.c
@@ -0,0 +1,227 @@
+/* $Id$ */
+/* $NetBSD: cmdtab.c,v 1.15 1997/04/05 03:27:33 lukem Exp $ */
+
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)cmdtab.c 8.4 (Berkeley) 10/9/94";
+#else
+static char rcsid[] = "$Id$";
+#endif
+#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 ftp server";
+char crhelp[] = "toggle carriage return stripping on ascii gets";
+char debughelp[] = "toggle/set debugging mode";
+char deletehelp[] = "delete remote file";
+char dirhelp[] = "list contents of remote directory";
+char disconhelp[] = "terminate ftp session";
+char domachelp[] = "execute macro";
+#ifndef SMALL
+char edithelp[] = "toggle command line editing";
+#endif /* !SMALL */
+char formhelp[] = "set file transfer format";
+char globhelp[] = "toggle metacharacter expansion of local file names";
+char hashhelp[] = "toggle printing `#' marks; specify number to set size";
+char helphelp[] = "print local help information";
+char idlehelp[] = "get (set) idle timer on remote side";
+char lcdhelp[] = "change local working directory";
+char lpwdhelp[] = "print 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 modehelp[] = "set file transfer mode";
+char modtimehelp[] = "show last modification time of remote file";
+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 pagehelp[] = "view a remote file through your pager";
+char passivehelp[] = "enter passive transfer mode";
+char porthelp[] = "toggle use of PORT cmd for each data connection";
+char preservehelp[] ="toggle preservation of modification time of "
+ "retreived files";
+char progresshelp[] ="toggle transfer progress meter";
+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 resethelp[] = "clear queued command replies";
+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 sendhelp[] = "send one file";
+char shellhelp[] = "escape to the shell";
+char sitehelp[] = "send site specific command to remote server\n"
+ "\t\tTry \"rhelp site\" or \"site help\" "
+ "for more information";
+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";
+
+#ifdef SMALL
+#define CMPL(x)
+#define CMPL0
+#else /* !SMALL */
+#define CMPL(x) __STRING(x),
+#define CMPL0 "",
+#endif /* !SMALL */
+
+struct cmd cmdtab[] = {
+ { "!", shellhelp, 0, 0, 0, CMPL0 shell },
+ { "$", domachelp, 1, 0, 0, CMPL0 domacro },
+ { "account", accounthelp, 0, 1, 1, CMPL0 account},
+ { "append", appendhelp, 1, 1, 1, CMPL(lr) put },
+ { "ascii", asciihelp, 0, 1, 1, CMPL0 setascii },
+ { "bell", beephelp, 0, 0, 0, CMPL0 setbell },
+ { "binary", binaryhelp, 0, 1, 1, CMPL0 setbinary },
+ { "bye", quithelp, 0, 0, 0, CMPL0 quit },
+ { "case", casehelp, 0, 0, 1, CMPL0 setcase },
+ { "cd", cdhelp, 0, 1, 1, CMPL(r) cd },
+ { "cdup", cduphelp, 0, 1, 1, CMPL0 cdup },
+ { "chmod", chmodhelp, 0, 1, 1, CMPL(nr) do_chmod },
+ { "close", disconhelp, 0, 1, 1, CMPL0 disconnect },
+ { "cr", crhelp, 0, 0, 0, CMPL0 setcr },
+ { "debug", debughelp, 0, 0, 0, CMPL0 setdebug },
+ { "delete", deletehelp, 0, 1, 1, CMPL(r) delete },
+ { "dir", dirhelp, 1, 1, 1, CMPL(rl) ls },
+ { "disconnect", disconhelp, 0, 1, 1, CMPL0 disconnect },
+#ifndef SMALL
+ { "edit", edithelp, 0, 0, 0, CMPL0 setedit },
+#endif /* !SMALL */
+ { "exit", quithelp, 0, 0, 0, CMPL0 quit },
+ { "form", formhelp, 0, 1, 1, CMPL0 setform },
+ { "ftp", connecthelp, 0, 0, 1, CMPL0 setpeer },
+ { "get", receivehelp, 1, 1, 1, CMPL(rl) get },
+ { "glob", globhelp, 0, 0, 0, CMPL0 setglob },
+ { "hash", hashhelp, 0, 0, 0, CMPL0 sethash },
+ { "help", helphelp, 0, 0, 1, CMPL(C) help },
+ { "idle", idlehelp, 0, 1, 1, CMPL0 idle },
+ { "image", binaryhelp, 0, 1, 1, CMPL0 setbinary },
+ { "lcd", lcdhelp, 0, 0, 0, CMPL(l) lcd },
+ { "less", pagehelp, 1, 1, 1, CMPL(r) page },
+ { "lpwd", lpwdhelp, 0, 0, 0, CMPL0 lpwd },
+ { "ls", lshelp, 1, 1, 1, CMPL(rl) ls },
+ { "macdef", macdefhelp, 0, 0, 0, CMPL0 macdef },
+ { "mdelete", mdeletehelp, 1, 1, 1, CMPL(R) mdelete },
+ { "mdir", mdirhelp, 1, 1, 1, CMPL(R) mls },
+ { "mget", mgethelp, 1, 1, 1, CMPL(R) mget },
+ { "mkdir", mkdirhelp, 0, 1, 1, CMPL(r) makedir },
+ { "mls", mlshelp, 1, 1, 1, CMPL(R) mls },
+ { "mode", modehelp, 0, 1, 1, CMPL0 setftmode },
+ { "modtime", modtimehelp, 0, 1, 1, CMPL(r) modtime },
+ { "more", pagehelp, 1, 1, 1, CMPL(r) page },
+ { "mput", mputhelp, 1, 1, 1, CMPL(L) mput },
+ { "msend", mputhelp, 1, 1, 1, CMPL(L) mput },
+ { "newer", newerhelp, 1, 1, 1, CMPL(r) newer },
+ { "nlist", nlisthelp, 1, 1, 1, CMPL(rl) ls },
+ { "nmap", nmaphelp, 0, 0, 1, CMPL0 setnmap },
+ { "ntrans", ntranshelp, 0, 0, 1, CMPL0 setntrans },
+ { "open", connecthelp, 0, 0, 1, CMPL0 setpeer },
+ { "page", pagehelp, 1, 1, 1, CMPL(r) page },
+ { "passive", passivehelp, 0, 0, 0, CMPL0 setpassive },
+ { "preserve", preservehelp, 0, 0, 0, CMPL0 setpreserve },
+ { "progress", progresshelp, 0, 0, 0, CMPL0 setprogress },
+ { "prompt", prompthelp, 0, 0, 0, CMPL0 setprompt },
+ { "proxy", proxyhelp, 0, 0, 1, CMPL(c) doproxy },
+ { "put", sendhelp, 1, 1, 1, CMPL(lr) put },
+ { "pwd", pwdhelp, 0, 1, 1, CMPL0 pwd },
+ { "quit", quithelp, 0, 0, 0, CMPL0 quit },
+ { "quote", quotehelp, 1, 1, 1, CMPL0 quote },
+ { "recv", receivehelp, 1, 1, 1, CMPL(rl) get },
+ { "reget", regethelp, 1, 1, 1, CMPL(rl) reget },
+ { "rename", renamehelp, 0, 1, 1, CMPL(rr) renamefile },
+ { "reset", resethelp, 0, 1, 1, CMPL0 reset },
+ { "restart", restarthelp, 1, 1, 1, CMPL0 restart },
+ { "restrict", restricthelp, 0, 0, 0, CMPL0 setrestrict },
+ { "rhelp", remotehelp, 0, 1, 1, CMPL0 rmthelp },
+ { "rmdir", rmdirhelp, 0, 1, 1, CMPL(r) removedir },
+ { "rstatus", rmtstatushelp, 0, 1, 1, CMPL(r) rmtstatus },
+ { "runique", runiquehelp, 0, 0, 1, CMPL0 setrunique },
+ { "send", sendhelp, 1, 1, 1, CMPL(lr) put },
+ { "sendport", porthelp, 0, 0, 0, CMPL0 setport },
+ { "site", sitehelp, 0, 1, 1, CMPL0 site },
+ { "size", sizecmdhelp, 1, 1, 1, CMPL(r) sizecmd },
+ { "status", statushelp, 0, 0, 1, CMPL0 status },
+ { "struct", structhelp, 0, 1, 1, CMPL0 setstruct },
+ { "sunique", suniquehelp, 0, 0, 1, CMPL0 setsunique },
+ { "system", systemhelp, 0, 1, 1, CMPL0 syst },
+ { "tenex", tenexhelp, 0, 1, 1, CMPL0 settenex },
+ { "trace", tracehelp, 0, 0, 0, CMPL0 settrace },
+ { "type", typehelp, 0, 1, 1, CMPL0 settype },
+ { "umask", umaskhelp, 0, 1, 1, CMPL0 do_umask },
+ { "user", userhelp, 0, 1, 1, CMPL0 user },
+ { "verbose", verbosehelp, 0, 0, 0, CMPL0 setverbose },
+ { "?", helphelp, 0, 0, 1, CMPL(C) help },
+ { 0 },
+};
+
+int NCMDS = (sizeof(cmdtab) / sizeof(cmdtab[0])) - 1;
diff --git a/usr.bin/ftp/complete.c b/usr.bin/ftp/complete.c
new file mode 100644
index 0000000..689c1f8
--- /dev/null
+++ b/usr.bin/ftp/complete.c
@@ -0,0 +1,367 @@
+/* $Id: complete.c,v 1.2 1997/06/27 09:30:04 ache Exp $ */
+/* $NetBSD: complete.c,v 1.8 1997/05/24 16:34:30 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1997 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SMALL
+#ifndef lint
+static char rcsid[] = "$Id: complete.c,v 1.2 1997/06/27 09:30:04 ache Exp $";
+#endif /* not lint */
+
+/*
+ * FTP user program - command and file completion routines
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <err.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ftp_var.h"
+
+static int
+comparstr(a, b)
+ const void *a, *b;
+{
+ return (strcoll(*(char **)a, *(char **)b));
+}
+
+/*
+ * Determine if complete is ambiguous. If unique, insert.
+ * If no choices, error. If unambiguous prefix, insert that.
+ * Otherwise, list choices. words is assumed to be filtered
+ * to only contain possible choices.
+ * Args:
+ * word word which started the match
+ * list list by default
+ * words stringlist containing possible matches
+ */
+static unsigned char
+complete_ambiguous(word, list, words)
+ char *word;
+ int list;
+ StringList *words;
+{
+ char insertstr[MAXPATHLEN];
+ char *lastmatch;
+ int i, j, matchlen, wordlen;
+
+ wordlen = strlen(word);
+ if (words->sl_cur == 0)
+ return (CC_ERROR); /* no choices available */
+
+ if (words->sl_cur == 1) { /* only once choice available */
+ (void)strcpy(insertstr, words->sl_str[0]);
+ if (el_insertstr(el, insertstr + wordlen) == -1)
+ return (CC_ERROR);
+ else
+ return (CC_REFRESH);
+ }
+
+ if (!list) {
+ matchlen = 0;
+ lastmatch = words->sl_str[0];
+ matchlen = strlen(lastmatch);
+ for (i = 1 ; i < words->sl_cur ; i++) {
+ for (j = wordlen ; j < strlen(words->sl_str[i]); j++)
+ if (lastmatch[j] != words->sl_str[i][j])
+ break;
+ if (j < matchlen)
+ matchlen = j;
+ }
+ if (matchlen > wordlen) {
+ (void)strncpy(insertstr, lastmatch, matchlen);
+ insertstr[matchlen] = '\0';
+ if (el_insertstr(el, insertstr + wordlen) == -1)
+ return (CC_ERROR);
+ else
+ /*
+ * XXX: really want CC_REFRESH_BEEP
+ */
+ return (CC_REFRESH);
+ }
+ }
+
+ putchar('\n');
+ qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr);
+ list_vertical(words);
+ return (CC_REDISPLAY);
+}
+
+/*
+ * Complete a command
+ */
+static unsigned char
+complete_command(word, list)
+ char *word;
+ int list;
+{
+ struct cmd *c;
+ StringList *words;
+ int wordlen;
+ unsigned char rv;
+
+ words = sl_init();
+ wordlen = strlen(word);
+
+ for (c = cmdtab; c->c_name != NULL; c++) {
+ if (wordlen > strlen(c->c_name))
+ continue;
+ if (strncmp(word, c->c_name, wordlen) == 0)
+ sl_add(words, c->c_name);
+ }
+
+ rv = complete_ambiguous(word, list, words);
+ sl_free(words, 0);
+ return (rv);
+}
+
+/*
+ * Complete a local file
+ */
+static unsigned char
+complete_local(word, list)
+ char *word;
+ int list;
+{
+ StringList *words;
+ char dir[MAXPATHLEN];
+ char *file;
+ DIR *dd;
+ struct dirent *dp;
+ unsigned char rv;
+
+ if ((file = strrchr(word, '/')) == NULL) {
+ dir[0] = '.';
+ dir[1] = '\0';
+ file = word;
+ } else {
+ if (file == word) {
+ dir[0] = '/';
+ dir[1] = '\0';
+ } else {
+ (void)strncpy(dir, word, file - word);
+ dir[file - word] = '\0';
+ }
+ file++;
+ }
+
+ if ((dd = opendir(dir)) == NULL)
+ return (CC_ERROR);
+
+ words = sl_init();
+
+ for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) {
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+ continue;
+ if (strlen(file) > dp->d_namlen)
+ continue;
+ if (strncmp(file, dp->d_name, strlen(file)) == 0) {
+ char *tcp;
+
+ tcp = strdup(dp->d_name);
+ if (tcp == NULL)
+ errx(1, "Can't allocate memory for local dir");
+ sl_add(words, tcp);
+ }
+ }
+ closedir(dd);
+
+ rv = complete_ambiguous(file, list, words);
+ sl_free(words, 1);
+ return (rv);
+}
+
+/*
+ * Complete a remote file
+ */
+static unsigned char
+complete_remote(word, list)
+ char *word;
+ int list;
+{
+ static StringList *dirlist;
+ static char lastdir[MAXPATHLEN];
+ StringList *words;
+ char dir[MAXPATHLEN];
+ char *file, *cp;
+ int i;
+ unsigned char rv;
+
+ char *dummyargv[] = { "complete", dir, NULL };
+
+ if ((file = strrchr(word, '/')) == NULL) {
+ dir[0] = '.';
+ dir[1] = '\0';
+ file = word;
+ } else {
+ cp = file;
+ while (*cp == '/' && cp > word)
+ cp--;
+ (void)strncpy(dir, word, cp - word + 1);
+ dir[cp - word + 1] = '\0';
+ file++;
+ }
+
+ if (dirchange || strcmp(dir, lastdir) != 0) { /* dir not cached */
+ char *emesg;
+
+ if (dirlist != NULL)
+ sl_free(dirlist, 1);
+ dirlist = sl_init();
+
+ mflag = 1;
+ emesg = NULL;
+ while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) {
+ char *tcp;
+
+ if (!mflag)
+ continue;
+ if (*cp == '\0') {
+ mflag = 0;
+ continue;
+ }
+ tcp = strrchr(cp, '/');
+ if (tcp)
+ tcp++;
+ else
+ tcp = cp;
+ tcp = strdup(tcp);
+ if (tcp == NULL)
+ errx(1, "Can't allocate memory for remote dir");
+ sl_add(dirlist, tcp);
+ }
+ if (emesg != NULL) {
+ printf("\n%s\n", emesg);
+ return (CC_REDISPLAY);
+ }
+ (void)strcpy(lastdir, dir);
+ dirchange = 0;
+ }
+
+ words = sl_init();
+ for (i = 0; i < dirlist->sl_cur; i++) {
+ cp = dirlist->sl_str[i];
+ if (strlen(file) > strlen(cp))
+ continue;
+ if (strncmp(file, cp, strlen(file)) == 0)
+ sl_add(words, cp);
+ }
+ rv = complete_ambiguous(file, list, words);
+ sl_free(words, 0);
+ return (rv);
+}
+
+/*
+ * Generic complete routine
+ */
+unsigned char
+complete(el, ch)
+ EditLine *el;
+ int ch;
+{
+ static char word[FTPBUFLEN];
+ static int lastc_argc, lastc_argo;
+
+ struct cmd *c;
+ const LineInfo *lf;
+ int len, celems, dolist;
+
+ lf = el_line(el);
+ len = lf->lastchar - lf->buffer;
+ if (len >= sizeof(line))
+ return (CC_ERROR);
+ (void)strncpy(line, lf->buffer, len);
+ line[len] = '\0';
+ cursor_pos = line + (lf->cursor - lf->buffer);
+ lastc_argc = cursor_argc; /* remember last cursor pos */
+ lastc_argo = cursor_argo;
+ makeargv(); /* build argc/argv of current line */
+
+ if (cursor_argo >= sizeof(word))
+ return (CC_ERROR);
+
+ dolist = 0;
+ /* if cursor and word is same, list alternatives */
+ if (lastc_argc == cursor_argc && lastc_argo == cursor_argo
+ && strncmp(word, margv[cursor_argc], cursor_argo) == 0)
+ dolist = 1;
+ else
+ (void)strncpy(word, margv[cursor_argc], cursor_argo);
+ word[cursor_argo] = '\0';
+
+ if (cursor_argc == 0)
+ return (complete_command(word, dolist));
+
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1 || c == 0)
+ return (CC_ERROR);
+ celems = strlen(c->c_complete);
+
+ /* check for 'continuation' completes (which are uppercase) */
+ if ((cursor_argc > celems) && (celems > 0)
+ && isupper((unsigned char)c->c_complete[celems-1]))
+ cursor_argc = celems;
+
+ if (cursor_argc > celems)
+ return (CC_ERROR);
+
+ switch (c->c_complete[cursor_argc - 1]) {
+ case 'l': /* local complete */
+ case 'L':
+ return (complete_local(word, dolist));
+ case 'r': /* remote complete */
+ case 'R':
+ if (connected != -1) {
+ puts("\nMust be logged in to complete.");
+ return (CC_REDISPLAY);
+ }
+ return (complete_remote(word, dolist));
+ case 'c': /* command complete */
+ case 'C':
+ return (complete_command(word, dolist));
+ case 'n': /* no complete */
+ default:
+ return (CC_ERROR);
+ }
+
+ return (CC_ERROR);
+}
+#endif
diff --git a/usr.bin/ftp/domacro.c b/usr.bin/ftp/domacro.c
new file mode 100644
index 0000000..a28d068
--- /dev/null
+++ b/usr.bin/ftp/domacro.c
@@ -0,0 +1,154 @@
+/* $Id: domacro.c,v 1.3 1997/06/27 09:30:09 ache Exp $ */
+/* $NetBSD: domacro.c,v 1.9 1997/03/13 06:23:14 lukem Exp $ */
+
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)domacro.c 8.3 (Berkeley) 4/2/94";
+#else
+static char rcsid[] = "$Id: domacro.c,v 1.3 1997/06/27 09:30:09 ache Exp $";
+#endif
+#endif /* not lint */
+
+#include <ctype.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.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 (isascii(*cp1) && isspace(*cp1)) {
+ cp1++;
+ }
+ cp2 = line;
+ while (*cp1 != '\0') {
+ switch(*cp1) {
+ case '\\':
+ *cp2++ = *++cp1;
+ break;
+ case '$':
+ if (isdigit((unsigned char)*(cp1+1))) {
+ j = 0;
+ while (isdigit((unsigned char)*++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) {
+ puts("?Ambiguous command.");
+ code = -1;
+ }
+ else if (c == 0) {
+ puts("?Invalid command.");
+ code = -1;
+ }
+ else if (c->c_conn && !connected) {
+ puts("Not connected.");
+ code = -1;
+ }
+ else {
+ if (verbose)
+ puts(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..3acc671
--- /dev/null
+++ b/usr.bin/ftp/extern.h
@@ -0,0 +1,180 @@
+/* $Id$ */
+/* $NetBSD: extern.h,v 1.15 1997/04/14 09:09:17 lukem Exp $ */
+
+/*-
+ * 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.3 (Berkeley) 10/9/94
+ */
+
+struct fd_set;
+
+void abort_remote __P((FILE *));
+void abortpt __P((int));
+void abortrecv __P((int));
+void abortsend __P((int));
+void aborthttp __P((int));
+void account __P((int, char **));
+void alarmtimer __P((int));
+int another __P((int *, char ***, const char *));
+int auto_fetch __P((int, char **));
+void blkfree __P((char **));
+void cd __P((int, char **));
+void cdup __P((int, char **));
+void changetype __P((int, int));
+void cmdabort __P((int));
+void cmdscanner __P((int));
+int command __P((const char *, ...));
+#ifndef SMALL
+unsigned char complete __P((EditLine *, int));
+void controlediting __P((void));
+#endif /* !SMALL */
+int confirm __P((const char *, const char *));
+FILE *dataconn __P((const 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 get __P((int, char **));
+struct cmd *getcmd __P((const char *));
+int getit __P((int, char **, int, const char *));
+int getreply __P((int));
+int globulize __P((char **));
+char *gunique __P((const char *));
+void help __P((int, char **));
+char *hookup __P((const char *, int));
+void idle __P((int, char **));
+int initconn __P((void));
+void intr __P((void));
+void list_vertical __P((StringList *));
+void lcd __P((int, char **));
+int login __P((const char *, char *, char *));
+void lostpeer __P((void));
+void lpwd __P((int, char **));
+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 page __P((int, char **));
+void progressmeter __P((int));
+char *prompt __P((void));
+void proxabort __P((int));
+void proxtrans __P((const char *, const char *, const char *));
+void psabort __P((int));
+void psummary __P((int));
+void pswitch __P((int));
+void ptransfer __P((int));
+void put __P((int, char **));
+void pwd __P((int, char **));
+void quit __P((int, char **));
+void quote __P((int, char **));
+void quote1 __P((const char *, int, char **));
+void recvrequest __P((const char *, const char *, const char *,
+ const char *, int));
+void reget __P((int, char **));
+char *remglob __P((char **, int, char **));
+off_t remotesize __P((const char *, int));
+time_t remotemodtime __P((const 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((const char *, char **, char **, char **));
+void sendrequest __P((const char *, const char *, const 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 setedit __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((int, char **));
+void setpeer __P((int, char **));
+void setport __P((int, char **));
+void setpreserve __P((int, char **));
+void setprogress __P((int, char **));
+void setprompt __P((int, char **));
+void setrestrict __P((int, char **));
+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 setttywidth __P((int));
+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 **));
+int togglevar __P((int, char **, int *, const char *));
+void usage __P((void));
+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;
+
+extern char *__progname; /* from crt0.o */
+
diff --git a/usr.bin/ftp/fetch.c b/usr.bin/ftp/fetch.c
new file mode 100644
index 0000000..e79c0a7
--- /dev/null
+++ b/usr.bin/ftp/fetch.c
@@ -0,0 +1,608 @@
+/* $Id: fetch.c,v 1.1 1997/06/25 08:56:39 msmith Exp $ */
+/* $NetBSD: fetch.c,v 1.10 1997/05/23 18:54:18 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1997 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason Thorpe and Luke Mewburn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * 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: fetch.c,v 1.1 1997/06/25 08:56:39 msmith Exp $";
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Command line file retrieval
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <arpa/ftp.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ftp_var.h"
+
+#define FTP_URL "ftp://" /* ftp URL prefix */
+#define HTTP_URL "http://" /* http URL prefix */
+#define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */
+#define HTTP_PROXY "http_proxy" /* env var with http proxy location */
+
+
+#define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0'))
+
+jmp_buf httpabort;
+
+/*
+ * Retrieve URL, via the proxy in $proxyvar if necessary.
+ * Modifies the string argument given.
+ * Returns -1 on failure, 0 on success
+ */
+int
+url_get(origline, proxyenv)
+ const char *origline;
+ const char *proxyenv;
+{
+ struct sockaddr_in sin;
+ int i, out, port, s;
+ size_t buflen, len;
+ char c, *cp, *cp2, *savefile, *portnum, *path, buf[4096];
+ char *line, *proxy, *host;
+ sig_t oldintr;
+ off_t hashbytes;
+
+ s = -1;
+ proxy = NULL;
+
+ line = strdup(origline);
+ if (line == NULL)
+ errx(1, "Can't allocate memory to parse URL");
+ if (strncasecmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0)
+ host = line + sizeof(HTTP_URL) - 1;
+ else if (strncasecmp(line, FTP_URL, sizeof(FTP_URL) - 1) == 0)
+ host = line + sizeof(FTP_URL) - 1;
+ else
+ errx(1, "url_get: Invalid URL '%s'", line);
+
+ path = strchr(host, '/'); /* find path */
+ if (EMPTYSTRING(path)) {
+ warnx("Invalid URL: %s", origline);
+ goto cleanup_url_get;
+ }
+ *path++ = '\0';
+ if (EMPTYSTRING(path)) {
+ warnx("Invalid URL: %s", origline);
+ goto cleanup_url_get;
+ }
+
+ savefile = strrchr(path, '/'); /* find savefile */
+ if (savefile != NULL)
+ savefile++;
+ else
+ savefile = path;
+ if (EMPTYSTRING(savefile)) {
+ warnx("Invalid URL: %s", origline);
+ goto cleanup_url_get;
+ }
+
+ if (proxyenv != NULL) { /* use proxy */
+ proxy = strdup(proxyenv);
+ if (proxy == NULL)
+ errx(1, "Can't allocate memory for proxy URL.");
+ if (strncasecmp(proxy, HTTP_URL, sizeof(HTTP_URL) - 1) == 0)
+ host = proxy + sizeof(HTTP_URL) - 1;
+ else if (strncasecmp(proxy, FTP_URL, sizeof(FTP_URL) - 1) == 0)
+ host = proxy + sizeof(FTP_URL) - 1;
+ else {
+ warnx("Malformed proxy URL: %s", proxyenv);
+ goto cleanup_url_get;
+ }
+ if (EMPTYSTRING(host)) {
+ warnx("Malformed proxy URL: %s", proxyenv);
+ goto cleanup_url_get;
+ }
+ *--path = '/'; /* add / back to real path */
+ path = strchr(host, '/'); /* remove trailing / on host */
+ if (! EMPTYSTRING(path))
+ *path++ = '\0';
+ path = line;
+ }
+
+ portnum = strchr(host, ':'); /* find portnum */
+ if (portnum != NULL)
+ *portnum++ = '\0';
+
+ if (debug)
+ printf("host %s, port %s, path %s, save as %s.\n",
+ host, portnum, path, savefile);
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+
+ if (isdigit((unsigned char)host[0])) {
+ if (inet_aton(host, &sin.sin_addr) == 0) {
+ warnx("Invalid IP address: %s", host);
+ goto cleanup_url_get;
+ }
+ } else {
+ struct hostent *hp;
+
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ warnx("%s: %s", host, hstrerror(h_errno));
+ goto cleanup_url_get;
+ }
+ if (hp->h_addrtype != AF_INET) {
+ warnx("%s: not an Internet address?", host);
+ goto cleanup_url_get;
+ }
+ memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
+ }
+
+ if (! EMPTYSTRING(portnum)) {
+ port = atoi(portnum);
+ if (port < 1 || (port & 0xffff) != port) {
+ warnx("Invalid port: %s", portnum);
+ goto cleanup_url_get;
+ }
+ port = htons(port);
+ } else
+ port = httpport;
+ sin.sin_port = port;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s == -1) {
+ warn("Can't create socket");
+ goto cleanup_url_get;
+ }
+
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
+ warn("Can't connect to %s", host);
+ goto cleanup_url_get;
+ }
+
+ /*
+ * Construct and send the request. We're expecting a return
+ * status of "200". Proxy requests don't want leading /.
+ */
+ if (!proxy)
+ printf("Requesting %s\n", origline);
+ else
+ printf("Requesting %s (via %s)\n", origline, proxyenv);
+ snprintf(buf, sizeof(buf), "GET %s%s HTTP/1.0\n\n",
+ proxy ? "" : "/", path);
+ buflen = strlen(buf);
+ if (write(s, buf, buflen) < buflen) {
+ warn("Writing HTTP request");
+ goto cleanup_url_get;
+ }
+ memset(buf, 0, sizeof(buf));
+ for (i = 0, buflen = sizeof(buf), cp = buf; i < buflen; cp++, i++) {
+ if (read(s, cp, 1) != 1)
+ goto improper;
+ if (*cp == '\r')
+ continue;
+ if (*cp == '\n')
+ break;
+ }
+ buf[buflen - 1] = '\0'; /* sanity */
+ cp = strchr(buf, ' ');
+ if (cp == NULL)
+ goto improper;
+ else
+ cp++;
+ if (strncmp(cp, "200", 3)) {
+ warnx("Error retrieving file: %s", cp);
+ goto cleanup_url_get;
+ }
+
+ /*
+ * Read the rest of the header.
+ */
+ memset(buf, 0, sizeof(buf));
+ c = '\0';
+ for (i = 0, buflen = sizeof(buf), cp = buf; i < buflen; cp++, i++) {
+ if (read(s, cp, 1) != 1)
+ goto improper;
+ if (*cp == '\r')
+ continue;
+ if (*cp == '\n' && c == '\n')
+ break;
+ c = *cp;
+ }
+ buf[buflen - 1] = '\0'; /* sanity */
+
+ /*
+ * Look for the "Content-length: " header.
+ */
+#define CONTENTLEN "Content-Length: "
+ for (cp = buf; *cp != '\0'; cp++) {
+ if (tolower((unsigned char)*cp) == 'c' &&
+ strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0)
+ break;
+ }
+ if (*cp == '\0')
+ goto improper;
+ cp += sizeof(CONTENTLEN) - 1;
+ cp2 = strchr(cp, '\n');
+ if (cp2 == NULL)
+ goto improper;
+ else
+ *cp2 = '\0';
+ filesize = atoi(cp);
+ if (filesize < 1)
+ goto improper;
+
+ /* Open the output file. */
+ out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+ if (out < 0) {
+ warn("Can't open %s", savefile);
+ goto cleanup_url_get;
+ }
+
+ /* Trap signals */
+ oldintr = NULL;
+ if (setjmp(httpabort)) {
+ if (oldintr)
+ (void)signal(SIGINT, oldintr);
+ goto cleanup_url_get;
+ }
+ oldintr = signal(SIGINT, aborthttp);
+
+ bytes = 0;
+ hashbytes = mark;
+ progressmeter(-1);
+
+ /* Finally, suck down the file. */
+ i = 0;
+ while ((len = read(s, buf, sizeof(buf))) > 0) {
+ bytes += len;
+ for (cp = buf; len > 0; len -= i, cp += i) {
+ if ((i = write(out, cp, len)) == -1) {
+ warn("Writing %s", savefile);
+ goto cleanup_url_get;
+ }
+ else if (i == 0)
+ break;
+ }
+ if (hash && !progress) {
+ while (bytes >= hashbytes) {
+ (void)putchar('#');
+ hashbytes += mark;
+ }
+ (void)fflush(stdout);
+ }
+ }
+ if (hash && !progress && bytes > 0) {
+ if (bytes < mark)
+ (void)putchar('#');
+ (void)putchar('\n');
+ (void)fflush(stdout);
+ }
+ if (len != 0) {
+ warn("Reading from socket");
+ goto cleanup_url_get;
+ }
+ progressmeter(1);
+ if (verbose)
+ puts("Successfully retrieved file.");
+ (void)signal(SIGINT, oldintr);
+
+ close(s);
+ close(out);
+ if (proxy)
+ free(proxy);
+ free(line);
+ return (0);
+
+improper:
+ warnx("Improper response from %s", host);
+cleanup_url_get:
+ if (s != -1)
+ close(s);
+ if (proxy)
+ free(proxy);
+ free(line);
+ return (-1);
+}
+
+/*
+ * Abort a http retrieval
+ */
+void
+aborthttp(notused)
+ int notused;
+{
+
+ alarmtimer(0);
+ puts("\nhttp fetch aborted.");
+ (void)fflush(stdout);
+ longjmp(httpabort, 1);
+}
+
+/*
+ * Retrieve multiple files from the command line, transferring
+ * files of the form "host:path", "ftp://host/path" using the
+ * ftp protocol, and files of the form "http://host/path" using
+ * the http protocol.
+ * If path has a trailing "/", then return (-1);
+ * the path will be cd-ed into and the connection remains open,
+ * and the function will return -1 (to indicate the connection
+ * is alive).
+ * If an error occurs the return value will be the offset+1 in
+ * argv[] of the file that caused a problem (i.e, argv[x]
+ * returns x+1)
+ * Otherwise, 0 is returned if all files retrieved successfully.
+ */
+int
+auto_fetch(argc, argv)
+ int argc;
+ char *argv[];
+{
+ static char lasthost[MAXHOSTNAMELEN];
+ char *xargv[5];
+ char *cp, *line, *host, *dir, *file, *portnum;
+ char *user, *pass;
+ char *ftpproxy, *httpproxy;
+ int rval, xargc, argpos;
+ int dirhasglob, filehasglob;
+ char rempath[MAXPATHLEN];
+
+ argpos = 0;
+
+ if (setjmp(toplevel)) {
+ if (connected)
+ disconnect(0, NULL);
+ return (argpos + 1);
+ }
+ (void)signal(SIGINT, (sig_t)intr);
+ (void)signal(SIGPIPE, (sig_t)lostpeer);
+
+ ftpproxy = getenv(FTP_PROXY);
+ httpproxy = getenv(HTTP_PROXY);
+
+ /*
+ * Loop through as long as there's files to fetch.
+ */
+ for (rval = 0; (rval == 0) && (argpos < argc); free(line), argpos++) {
+ if (strchr(argv[argpos], ':') == NULL)
+ break;
+ host = dir = file = portnum = user = pass = NULL;
+
+ /*
+ * We muck with the string, so we make a copy.
+ */
+ line = strdup(argv[argpos]);
+ if (line == NULL)
+ errx(1, "Can't allocate memory for auto-fetch.");
+
+ /*
+ * Try HTTP URL-style arguments first.
+ */
+ if (strncasecmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) {
+ if (url_get(line, httpproxy) == -1)
+ rval = argpos + 1;
+ continue;
+ }
+
+ /*
+ * Try FTP URL-style arguments next. If ftpproxy is
+ * set, use url_get() instead of standard ftp.
+ * Finally, try host:file.
+ */
+ host = line;
+ if (strncasecmp(line, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
+ if (ftpproxy) {
+ if (url_get(line, ftpproxy) == -1)
+ rval = argpos + 1;
+ continue;
+ }
+ host += sizeof(FTP_URL) - 1;
+ dir = strchr(host, '/');
+
+ /* look for [user:pass@]host[:port] */
+ pass = strpbrk(host, ":@/");
+ if (pass == NULL || *pass == '/') {
+ pass = NULL;
+ goto parsed_url;
+ }
+ if (pass == host || *pass == '@') {
+bad_ftp_url:
+ warnx("Invalid URL: %s", argv[argpos]);
+ rval = argpos + 1;
+ continue;
+ }
+ *pass++ = '\0';
+ cp = strpbrk(pass, ":@/");
+ if (cp == NULL || *cp == '/') {
+ portnum = pass;
+ pass = NULL;
+ goto parsed_url;
+ }
+ if (EMPTYSTRING(cp) || *cp == ':')
+ goto bad_ftp_url;
+ *cp++ = '\0';
+ user = host;
+ if (EMPTYSTRING(user))
+ goto bad_ftp_url;
+ host = cp;
+ portnum = strchr(host, ':');
+ if (portnum != NULL)
+ *portnum++ = '\0';
+parsed_url:
+ } else { /* classic style `host:file' */
+ dir = strchr(host, ':');
+ }
+ if (EMPTYSTRING(host)) {
+ rval = argpos + 1;
+ continue;
+ }
+
+ /*
+ * If cp is NULL, the file wasn't specified
+ * (URL looked something like ftp://host)
+ */
+ if (dir != NULL)
+ *dir++ = '\0';
+
+ /*
+ * Extract the file and (if present) directory name.
+ */
+ if (! EMPTYSTRING(dir)) {
+ cp = strrchr(dir, '/');
+ if (cp != NULL) {
+ *cp++ = '\0';
+ file = cp;
+ } else {
+ file = dir;
+ dir = NULL;
+ }
+ }
+ if (debug)
+ printf("user %s:%s host %s port %s dir %s file %s\n",
+ user, pass, host, portnum, dir, file);
+
+ /*
+ * Set up the connection if we don't have one.
+ */
+ if (strcmp(host, lasthost) != 0) {
+ int oautologin;
+
+ (void)strcpy(lasthost, host);
+ if (connected)
+ disconnect(0, NULL);
+ xargv[0] = __progname;
+ xargv[1] = host;
+ xargv[2] = NULL;
+ xargc = 2;
+ if (! EMPTYSTRING(portnum)) {
+ xargv[2] = portnum;
+ xargv[3] = NULL;
+ xargc = 3;
+ }
+ oautologin = autologin;
+ if (user != NULL)
+ autologin = 0;
+ setpeer(xargc, xargv);
+ autologin = oautologin;
+ if ((connected == 0)
+ || ((connected == 1) && !login(host, user, pass)) ) {
+ warnx("Can't connect or login to host `%s'",
+ host);
+ rval = argpos + 1;
+ continue;
+ }
+
+ /* Always use binary transfers. */
+ setbinary(0, NULL);
+ }
+ /* cd back to '/' */
+ xargv[0] = "cd";
+ xargv[1] = "/";
+ xargv[2] = NULL;
+ cd(2, xargv);
+ if (! dirchange) {
+ rval = argpos + 1;
+ continue;
+ }
+
+ dirhasglob = filehasglob = 0;
+ if (doglob) {
+ if (! EMPTYSTRING(dir) &&
+ strpbrk(dir, "*?[]{}") != NULL)
+ dirhasglob = 1;
+ if (! EMPTYSTRING(file) &&
+ strpbrk(file, "*?[]{}") != NULL)
+ filehasglob = 1;
+ }
+
+ /* Change directories, if necessary. */
+ if (! EMPTYSTRING(dir) && !dirhasglob) {
+ xargv[0] = "cd";
+ xargv[1] = dir;
+ xargv[2] = NULL;
+ cd(2, xargv);
+ if (! dirchange) {
+ rval = argpos + 1;
+ continue;
+ }
+ }
+
+ if (EMPTYSTRING(file)) {
+ rval = -1;
+ continue;
+ }
+
+ if (!verbose)
+ printf("Retrieving %s/%s\n", dir ? dir : "", file);
+
+ if (dirhasglob) {
+ snprintf(rempath, sizeof(rempath), "%s/%s", dir, file);
+ file = rempath;
+ }
+
+ /* Fetch the file(s). */
+ xargv[0] = "get";
+ xargv[1] = file;
+ xargv[2] = NULL;
+ if (dirhasglob || filehasglob) {
+ int ointeractive;
+
+ ointeractive = interactive;
+ interactive = 0;
+ xargv[0] = "mget";
+ mget(2, xargv);
+ interactive = ointeractive;
+ } else
+ get(2, xargv);
+
+ if ((code / 100) != COMPLETE)
+ rval = argpos + 1;
+ }
+ if (connected && rval != -1)
+ disconnect(0, NULL);
+ return (rval);
+}
diff --git a/usr.bin/ftp/ftp.1 b/usr.bin/ftp/ftp.1
new file mode 100644
index 0000000..c288d48
--- /dev/null
+++ b/usr.bin/ftp/ftp.1
@@ -0,0 +1,1377 @@
+.\" $Id$
+.\" $NetBSD: ftp.1,v 1.21 1997/06/10 21:59:58 lukem Exp $
+.\"
+.\" 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.3 (Berkeley) 10/9/94
+.\"
+.Dd February 23, 1997
+.Dt FTP 1
+.Os BSD 4.2
+.Sh NAME
+.Nm ftp
+.Nd
+.Tn ARPANET
+file transfer program
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl d
+.Op Fl e
+.Op Fl g
+.Op Fl i
+.Op Fl n
+.Op Fl U
+.Op Fl p
+.Op Fl P Ar port
+.Op Fl t
+.Op Fl v
+.Op Fl V
+.Op Ar host Op Ar port
+.Nm ftp
+ftp://[\fIuser\fR:\fIpassword\fR@]\fIhost\fR[:\fIport\fR]/\fIfile\fR[/]
+.Nm ftp
+http://\fIhost\fR[:\fIport\fR]/\fIfile\fR
+.Nm ftp
+\fIhost\fR:[/\fIpath\fR/]\fIfile\fR[/]
+.Sh DESCRIPTION
+.Nm
+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
+The latter three usage formats will fetch a file using either the
+HTTP or FTP protocols into the current directory.
+This is ideal for scripts.
+Refer to
+.Sx AUTO-FETCHING FILES
+below for more information.
+.Pp
+Options may be specified at the command line, or to the
+command interpreter.
+.Bl -tag -width "port "
+.It Fl a
+Causes
+.Nm
+to bypass normal login procedure, and use an anonymous login instead.
+.It Fl d
+Enables debugging.
+.It Fl e
+Disables command line editing.
+.It Fl g
+Disables file name globbing.
+.It Fl U
+Disable data port range restrictions.
+.It Fl i
+Turns off interactive prompting during
+multiple file transfers.
+.It Fl n
+Restrains
+.Nm
+from attempting
+.Dq auto-login
+upon initial connection.
+If auto-login is enabled,
+.Nm
+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
+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 p
+Enable passive mode operation for use behind connection filtering firewalls.
+.It Fl P Ar port
+Sets the port number to
+.Ar port .
+.It Fl t
+Enables packet tracing.
+.It Fl v
+Enable verbose mode.
+This is the default if input is from a terminal.
+Forces
+.Nm
+to show all responses from the remote server, as well
+as report on data transfer statistics.
+.It Fl V
+Disable verbose mode, overriding the default of enabled when input
+is from a terminal.
+.El
+.Pp
+The client host with which
+.Nm
+is to communicate may be specified on the command line.
+If this is done,
+.Nm
+will immediately attempt to establish a connection to an
+.Tn FTP
+server on that host; otherwise,
+.Nm
+will enter its command interpreter and await instructions
+from the user.
+When
+.Nm
+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
+system 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
+prints each command sent to the remote machine, preceded
+by the string
+.Ql \-\->
+.It Ic dir Op Ar remote-directory Op Ar local-file
+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 ls . )
+If
+.Ar remote-directory
+is left unspecified, the current working directory is used.
+If interactive prompting is on,
+.Nm
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic dir
+output.
+If no local file is specified, or if
+.Ar local-file
+is
+.Sq Fl ,
+the output is sent to the terminal.
+.It Ic disconnect
+A synonym for
+.Ic close .
+.It Ic edit
+Toggle command line editing, and context sensitive command and file
+completion.
+This is automatically enabled if input is from a terminal, and
+disabled otherwise.
+.It Ic exit
+A synonym for
+.Ic bye .
+.It Ic ftp Ar host Op Ar port
+A synonym for
+.Ic open .
+.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 Op Ar size
+Toggle hash-sign (``#'') printing for each data block
+transferred.
+The size of a data block defaults to 1024 bytes.
+This can be changed by specifying
+.Ar size
+in bytes.
+.It Ic help Op Ar command
+Print an informative message about the meaning of
+.Ar command .
+If no argument is given,
+.Nm
+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 Ic less Ar file
+A synonym for
+.Ic page .
+.It Ic lpwd
+Print the working directory on the local machine.
+.It Ic \&ls Op Ar remote-directory Op Ar local-file
+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
+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
+.Fl ,
+the output is sent to the terminal.
+.It Ic macdef 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
+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 ls ,
+except multiple remote files may be specified,
+and the
+.Ar local-file
+must be specified.
+If interactive prompting is on,
+.Nm
+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 more Ar file
+A synonym for
+.Ic page .
+.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 msend Ar local-files
+A synonym for
+.Ic mput .
+.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 Ic nlist Op Ar remote-directory Op Ar local-file
+A synonym for
+.Ic ls .
+.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
+will attempt to contact an
+.Tn FTP
+server at that port.
+If the
+.Ic auto-login
+option is on (default),
+.Nm
+will also attempt to automatically log the user in to
+the
+.Tn FTP
+server (see below).
+.It Ic page Ar file
+Retrieve
+.Ic file
+and display with the program defined in
+.Ev PAGER
+(which defaults to
+.Xr less 1 ).
+.It Ic passive
+Toggle passive mode. If passive mode is turned on
+(default is off), the ftp client will
+send a
+.Dv PASV
+command for all data connections instead of the usual
+.Dv PORT
+command. The
+.Dv PASV
+command requests that the remote server open a port for the data connection
+and return the address of that port. The remote server listens on that
+port and the client connects to it. When using the more traditional
+.Dv PORT
+command, the client listens on a port and sends that address to the remote
+server, who connects back to it. Passive mode is useful when using
+.Nm
+through a gateway router or host that controls the directionality of
+traffic.
+(Note that though ftp servers are required to support the
+.Dv PASV
+command by RFC 1123, some do not.)
+.It Ic preserve
+Toggle preservation of modification times on retrieved files.
+.It Ic progress
+Toggle display of transfer progress bar.
+The progress bar will be disabled for a transfer that has
+.Ar local-file
+as
+.Sq Fl
+or a command that starts with
+.Sq \&| .
+Refer to
+.Sx FILE NAMING CONVENTIONS
+for more information.
+.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.
+.Pp
+When prompting is on, the following commands are available at a prompt:
+.Bl -tag -width 2n -offset indent
+.It Ic n
+Do not transfer the file.
+.It Ic a
+Answer
+.Sq yes
+to the current file, and automatically answer
+.Sq yes
+to any remaining files for the current command.
+.It Ic p
+Answer
+.Sq yes
+to the current file, and turn off prompt mode
+(as is
+.Dq prompt off
+had been given).
+.El
+.Pp
+Any other reponse will answer
+.Sq yes
+to the current file.
+.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
+.Ic 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 rstatus 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 Ic rename Op Ar from Op Ar to
+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
+.Ic put .
+.It Ic sendport
+Toggle the use of
+.Dv PORT
+commands.
+By default,
+.Nm
+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
+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
+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
+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
+.Ic help .
+.El
+.Pp
+Command arguments which have embedded spaces may be quoted with
+quote `"' marks.
+.Pp
+Commands which toggle settings can take an explicit
+.Ic on
+or
+.Ic off
+argument to force the setting appropriately.
+.Pp
+If
+.Nm
+receives a
+.Dv SIGINFO
+(see the
+.Dq status
+argument of
+.Xr stty 1 )
+signal whilst a transfer is in progress, the current transfer rate
+statistics will be written to the standard error output, in the
+same format as the standard completion message.
+.Sh AUTO-FETCHING FILES
+In addition to standard commands, this version of
+.Nm
+supports an auto-fetch feature.
+To enable auto-fetch, simply pass the list of hostnames/files
+on the command line.
+.Pp
+The following formats are valid syntax for an auto-fetch element:
+.Bl -tag -width "host:/file"
+.It host:/file
+.Dq Classic
+ftp format
+.It ftp://[user:password@]host[:port]/file
+An ftp URL, retrieved using the ftp protocol if
+.Ev ftp_proxy
+isn't defined.
+Otherwise, transfer using http via the proxy defined in
+.Ev ftp_proxy .
+If
+.Ar user:password@
+is given and
+.Ev ftp_proxy
+isn't defined, login as
+.Ar user
+with a password of
+.Ar password .
+.It http://host[:port]/file
+An HTTP URL, retrieved using the http protocol.
+If
+.Ev http_proxy
+is defined, it is used as a URL to an HTTP proxy server.
+.El
+.Pp
+If a classic format or a ftp URL format has a trailing
+.Sq / ,
+then
+.Nm
+will connect to the site and
+.Ic cd
+to the directory given as the path, and leave the user in interactive
+mode ready for further input.
+.Pp
+If successive auto-fetch ftp elements refer to the same host, then
+the connection is maintained between transfers, reducing overhead on
+connection creation and deletion.
+.Pp
+If
+.Ic file
+contains a glob character and globbing is enabled,
+(see
+.Ic glob ) ,
+then the equivalent of
+.Ic "mget file"
+is performed.
+.Pp
+If the directory component of
+.Ic file
+contains no globbing characters,
+it is stored in the current directory as the
+.Xr basename 1
+of
+.Ic file .
+Otherwise, the remote name is used as the local name.
+.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
+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
+program must be killed by hand.
+.Sh FILE NAMING CONVENTIONS
+Files specified as arguments to
+.Nm
+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
+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
+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
+supports the ascii and image types of file transfer,
+plus local byte size 8 for
+.Ic tenex
+mode transfers.
+.Pp
+.Nm
+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
+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
+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
+.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 COMMAND LINE EDITING
+.Nm
+supports interactive command line editing, via the
+.Xr editline 3
+library.
+It is enabled with the
+.Ic edit
+command, and is enabled by default if input is from a tty.
+Previous lines can be recalled and edited with the arrow keys,
+and other GNU Emacs-style editing keys may be used as well.
+.Pp
+The
+.Xr editline 3
+library is configured with a
+.Pa .editrc
+file - refer to
+.Xr editrc 5
+for more information.
+.Pp
+An extra key binding is available to
+.Nm
+to provide context sensitive command and filename completion
+(including remote file completion).
+To use this, bind a key to the
+.Xr editline 3
+command
+.Ic ftp-complete .
+By default, this is bound to the TAB key.
+.Sh ENVIRONMENT
+.Nm
+utilizes the following environment variables.
+.Bl -tag -width "http_proxy"
+.It Ev HOME
+For default location of a
+.Pa .netrc
+file, if one exists.
+.It Ev PAGER
+Used by
+.Ic page
+to display files.
+.It Ev SHELL
+For default shell.
+.It Ev ftp_proxy
+URL of FTP proxy to use when making FTP URL requests
+(if not defined, use the standard ftp protocol).
+.It Ev http_proxy
+URL of HTTP proxy to use when making HTTP URL requests.
+.El
+.Sh SEE ALSO
+.Xr editrc 5 ,
+.Xr ftpd 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
+.Pp
+Various features such as command line editing, context sensitive
+command and file completion, dynamic progress bar, automatic
+fetching of files, ftp and http URLs, and modification time
+preservation were implemented in
+.Nx 1.3
+by Luke Mewburn, with assistance from Jason Thorpe.
+.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..f3914c1
--- /dev/null
+++ b/usr.bin/ftp/ftp.c
@@ -0,0 +1,1580 @@
+/* $Id$ */
+/* $NetBSD: ftp.c,v 1.25 1997/04/14 09:09:22 lukem Exp $ */
+
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)ftp.c 8.6 (Berkeley) 10/27/94";
+#else
+static char rcsid[] = "$Id$";
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.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 <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "ftp_var.h"
+
+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)
+ const char *host;
+ int port;
+{
+ struct hostent *hp = 0;
+ int s, len, tos;
+ static char hostnamebuf[MAXHOSTNAMELEN];
+
+ memset((void *)&hisctladdr, 0, sizeof(hisctladdr));
+ if (inet_aton(host, &hisctladdr.sin_addr) != 0) {
+ hisctladdr.sin_family = AF_INET;
+ (void)strncpy(hostnamebuf, host, sizeof(hostnamebuf) - 1);
+ hostnamebuf[sizeof(hostnamebuf) - 1] = '\0';
+ } 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;
+ memcpy(&hisctladdr.sin_addr, hp->h_addr, hp->h_length);
+ (void)strncpy(hostnamebuf, hp->h_name, sizeof(hostnamebuf) - 1);
+ hostnamebuf[sizeof(hostnamebuf) - 1] = '\0';
+ }
+ 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++;
+ memcpy(&hisctladdr.sin_addr, hp->h_addr, hp->h_length);
+ printf("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);
+}
+
+void
+cmdabort(notused)
+ int notused;
+{
+
+ alarmtimer(0);
+ putchar('\n');
+ (void)fflush(stdout);
+ abrtflag++;
+ if (ptflag)
+ longjmp(ptabort, 1);
+}
+
+/*VARARGS*/
+int
+#ifdef __STDC__
+command(const char *fmt, ...)
+#else
+command(va_alist)
+ va_dcl
+#endif
+{
+ va_list ap;
+ int r;
+ sig_t oldintr;
+#ifndef __STDC__
+ const char *fmt;
+#endif
+
+ abrtflag = 0;
+ if (debug) {
+ fputs("---> ", stdout);
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+ fmt = va_arg(ap, const char *);
+#endif
+ if (strncmp("PASS ", fmt, 5) == 0)
+ fputs("PASS XXXX", stdout);
+ else if (strncmp("ACCT ", fmt, 5) == 0)
+ fputs("ACCT XXXX", stdout);
+ else
+ vprintf(fmt, ap);
+ va_end(ap);
+ putchar('\n');
+ (void)fflush(stdout);
+ }
+ if (cout == NULL) {
+ warnx("No control connection for command.");
+ code = -1;
+ return (0);
+ }
+ oldintr = signal(SIGINT, cmdabort);
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+#endif
+ vfprintf(cout, fmt, ap);
+ va_end(ap);
+ fputs("\r\n", cout);
+ (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]; /* first line of previous reply */
+
+int
+getreply(expecteof)
+ int expecteof;
+{
+ char current_line[BUFSIZ]; /* last line of previous reply */
+ int c, n, line;
+ int dig;
+ int originalcode = 0, continuation = 0;
+ sig_t oldintr;
+ int pflag = 0;
+ char *cp, *pt = pasv;
+
+ oldintr = signal(SIGINT, cmdabort);
+ for (line = 0 ;; line++) {
+ dig = n = code = 0;
+ cp = current_line;
+ 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) {
+ puts(
+"421 Service not available, remote server has closed connection.");
+ (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 < &current_line[sizeof(current_line) - 1])
+ *cp++ = c;
+ }
+ if (verbose > 0 || (verbose > -1 && n == '5')) {
+ (void)putchar(c);
+ (void)fflush (stdout);
+ }
+ if (line == 0) {
+ size_t len = cp - current_line;
+
+ if (len > sizeof(reply_string))
+ len = sizeof(reply_string);
+
+ (void)strncpy(reply_string, current_line, len);
+ reply_string[len] = '\0';
+ }
+ 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(notused)
+ int notused;
+{
+
+ alarmtimer(0);
+ mflag = 0;
+ abrtflag = 0;
+ puts("\nsend aborted\nwaiting for remote to finish abort.");
+ (void)fflush(stdout);
+ longjmp(sendabort, 1);
+}
+
+void
+sendrequest(cmd, local, remote, printnames)
+ const char *cmd, *local, *remote;
+ int printnames;
+{
+ struct stat st;
+ int c, d;
+ FILE *fin, *dout = 0;
+ int (*closefunc) __P((FILE *));
+ sig_t oldinti, oldintr, oldintp;
+ off_t hashbytes;
+ char *lmode, buf[BUFSIZ], *bufp;
+ int oprogress;
+
+ hashbytes = mark;
+ direction = "sent";
+ bytes = 0;
+ filesize = -1;
+ oprogress = progress;
+ 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;
+ oldinti = 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);
+ if (oldinti)
+ (void)signal(SIGINFO, oldinti);
+ progress = oprogress;
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, abortsend);
+ oldinti = signal(SIGINFO, psummary);
+ if (strcmp(local, "-") == 0) {
+ fin = stdin;
+ progress = 0;
+ } 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);
+ (void)signal(SIGINFO, oldinti);
+ code = -1;
+ return;
+ }
+ progress = 0;
+ closefunc = pclose;
+ } else {
+ fin = fopen(local, "r");
+ if (fin == NULL) {
+ warn("local: %s", local);
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ code = -1;
+ return;
+ }
+ closefunc = fclose;
+ if (fstat(fileno(fin), &st) < 0 ||
+ (st.st_mode & S_IFMT) != S_IFREG) {
+ printf("%s: not a plain file.\n", local);
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ fclose(fin);
+ code = -1;
+ return;
+ }
+ filesize = st.st_size;
+ }
+ if (initconn()) {
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ if (oldintp)
+ (void)signal(SIGPIPE, oldintp);
+ code = -1;
+ progress = oprogress;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ if (setjmp(sendabort))
+ goto abort;
+
+ if (restart_point &&
+ (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) {
+ int rc;
+
+ rc = -1;
+ 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;
+ progress = oprogress;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ if (command("REST %ld", (long) restart_point)
+ != CONTINUE) {
+ restart_point = 0;
+ progress = oprogress;
+ 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);
+ (void)signal(SIGINFO, oldinti);
+ progress = oprogress;
+ if (oldintp)
+ (void)signal(SIGPIPE, oldintp);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ } else
+ if (command("%s", cmd) != PRELIM) {
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ progress = oprogress;
+ if (oldintp)
+ (void)signal(SIGPIPE, oldintp);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ dout = dataconn(lmode);
+ if (dout == NULL)
+ goto abort;
+ progressmeter(-1);
+ 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 && (!progress || filesize < 0) ) {
+ while (bytes >= hashbytes) {
+ (void)putchar('#');
+ hashbytes += mark;
+ }
+ (void)fflush(stdout);
+ }
+ }
+ if (hash && (!progress || filesize < 0) && bytes > 0) {
+ if (bytes < mark)
+ (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 && (!progress || filesize < 0) &&
+ (bytes >= hashbytes)) {
+ (void)putchar('#');
+ (void)fflush(stdout);
+ hashbytes += mark;
+ }
+ if (ferror(dout))
+ break;
+ (void)putc('\r', dout);
+ bytes++;
+ }
+ (void)putc(c, dout);
+ bytes++;
+#if 0 /* this violates RFC */
+ if (c == '\r') {
+ (void)putc('\0', dout);
+ bytes++;
+ }
+#endif
+ }
+ if (hash && (!progress || filesize < 0)) {
+ 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;
+ }
+ progressmeter(1);
+ progress = oprogress;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ (void)fclose(dout);
+ (void)getreply(0);
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ if (oldintp)
+ (void)signal(SIGPIPE, oldintp);
+ if (bytes > 0)
+ ptransfer(0);
+ return;
+abort:
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ progress = oprogress;
+ 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(0);
+}
+
+jmp_buf recvabort;
+
+void
+abortrecv(notused)
+ int notused;
+{
+
+ alarmtimer(0);
+ mflag = 0;
+ abrtflag = 0;
+ puts("\nreceive aborted\nwaiting for remote to finish abort.");
+ (void)fflush(stdout);
+ longjmp(recvabort, 1);
+}
+
+void
+recvrequest(cmd, local, remote, lmode, printnames)
+ const char *cmd, *local, *remote, *lmode;
+ int printnames;
+{
+ FILE *fout, *din = 0;
+ int (*closefunc) __P((FILE *));
+ sig_t oldinti, oldintr, oldintp;
+ int c, d, is_retr, tcrflag, bare_lfs = 0;
+ static int bufsize;
+ static char *buf;
+ off_t hashbytes;
+ struct stat st;
+ time_t mtime;
+ struct timeval tval[2];
+ int oprogress;
+ int opreserve;
+
+ hashbytes = mark;
+ direction = "received";
+ bytes = 0;
+ filesize = -1;
+ oprogress = progress;
+ opreserve = preserve;
+ 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);
+ if (oldinti)
+ (void)signal(SIGINFO, oldinti);
+ progress = oprogress;
+ preserve = opreserve;
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, abortrecv);
+ oldinti = signal(SIGINFO, psummary);
+ 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);
+ (void)signal(SIGINFO, oldinti);
+ 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);
+ (void)signal(SIGINFO, oldinti);
+ code = -1;
+ return;
+ }
+ if (!runique && errno == EACCES &&
+ chmod(local, 0600) < 0) {
+ warn("local: %s", local);
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ code = -1;
+ return;
+ }
+ if (runique && errno == EACCES &&
+ (local = gunique(local)) == NULL) {
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ code = -1;
+ return;
+ }
+ }
+ else if (runique && (local = gunique(local)) == NULL) {
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ code = -1;
+ return;
+ }
+ }
+ if (!is_retr) {
+ if (curtype != TYPE_A)
+ changetype(TYPE_A, 0);
+ } else {
+ if (curtype != type)
+ changetype(type, 0);
+ filesize = remotesize(remote, 0);
+ }
+ if (initconn()) {
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ 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);
+ (void)signal(SIGINFO, oldinti);
+ return;
+ }
+ } else {
+ if (command("%s", cmd) != PRELIM) {
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ return;
+ }
+ }
+ din = dataconn("r");
+ if (din == NULL)
+ goto abort;
+ if (strcmp(local, "-") == 0) {
+ fout = stdout;
+ progress = 0;
+ preserve = 0;
+ } else if (*local == '|') {
+ oldintp = signal(SIGPIPE, SIG_IGN);
+ fout = popen(local + 1, "w");
+ if (fout == NULL) {
+ warn("%s", local+1);
+ goto abort;
+ }
+ progress = 0;
+ preserve = 0;
+ 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;
+ }
+ if ((st.st_mode & S_IFMT) != S_IFREG) {
+ progress = 0;
+ preserve = 0;
+ }
+ progressmeter(-1);
+ switch (curtype) {
+
+ case TYPE_I:
+ case TYPE_L:
+ if (restart_point &&
+ lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
+ warn("local: %s", local);
+ progress = oprogress;
+ preserve = opreserve;
+ 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 && (!progress || filesize < 0)) {
+ while (bytes >= hashbytes) {
+ (void)putchar('#');
+ hashbytes += mark;
+ }
+ (void)fflush(stdout);
+ }
+ }
+ if (hash && (!progress || filesize < 0) && bytes > 0) {
+ if (bytes < mark)
+ (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);
+ progress = oprogress;
+ preserve = opreserve;
+ if (closefunc != NULL)
+ (*closefunc)(fout);
+ return;
+ }
+ }
+ while ((c = getc(din)) != EOF) {
+ if (c == '\n')
+ bare_lfs++;
+ while (c == '\r') {
+ while (hash && (!progress || filesize < 0) &&
+ (bytes >= hashbytes)) {
+ (void)putchar('#');
+ (void)fflush(stdout);
+ hashbytes += mark;
+ }
+ 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);
+ puts("File may not have transferred correctly.");
+ }
+ if (hash && (!progress || filesize < 0)) {
+ 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;
+ }
+ progressmeter(1);
+ progress = oprogress;
+ preserve = opreserve;
+ if (closefunc != NULL)
+ (*closefunc)(fout);
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ if (oldintp)
+ (void)signal(SIGPIPE, oldintp);
+ (void)fclose(din);
+ (void)getreply(0);
+ if (bytes >= 0 && is_retr) {
+ if (bytes > 0)
+ ptransfer(0);
+ if (preserve && (closefunc == fclose)) {
+ mtime = remotemodtime(remote, 0);
+ if (mtime != -1) {
+ (void)gettimeofday(&tval[0],
+ (struct timezone *)0);
+ tval[1].tv_sec = mtime;
+ tval[1].tv_usec = 0;
+ if (utimes(local, tval) == -1) {
+ printf(
+ "Can't change modification time on %s to %s",
+ local, asctime(localtime(&mtime)));
+ }
+ }
+ }
+ }
+ return;
+
+abort:
+
+/* abort using RFC959 recommended IP,SYNC sequence */
+
+ progress = oprogress;
+ preserve = opreserve;
+ if (oldintp)
+ (void)signal(SIGPIPE, oldintp);
+ (void)signal(SIGINT, SIG_IGN);
+ if (!cpend) {
+ code = -1;
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+ 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(0);
+ (void)signal(SIGINT, oldintr);
+ (void)signal(SIGINFO, oldinti);
+}
+
+/*
+ * 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 tos, ports;
+ int a0, a1, a2, a3, p0, p1;
+
+ if (passivemode) {
+ data = socket(AF_INET, SOCK_STREAM, 0);
+ if (data < 0) {
+ warn("socket");
+ return (1);
+ }
+ if ((options & SO_DEBUG) &&
+ setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on,
+ sizeof(on)) < 0)
+ warn("setsockopt (ignored)");
+ if (command("PASV") != COMPLETE) {
+ puts("Passive mode refused.");
+ goto bad;
+ }
+
+ /*
+ * What we've got at this point is a string of comma
+ * separated one-byte unsigned integer values.
+ * 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",
+ &a0, &a1, &a2, &a3, &p0, &p1) != 6) {
+ puts(
+"Passive mode address scan failure. Shouldn't happen!");
+ goto bad;
+ }
+
+ memset(&data_addr, 0, sizeof(data_addr));
+ data_addr.sin_family = AF_INET;
+ a = (char *)&data_addr.sin_addr.s_addr;
+ a[0] = a0 & 0xff;
+ a[1] = a1 & 0xff;
+ a[2] = a2 & 0xff;
+ a[3] = a3 & 0xff;
+ p = (char *)&data_addr.sin_port;
+ p[0] = p0 & 0xff;
+ p[1] = p1 & 0xff;
+
+ if (connect(data, (struct sockaddr *)&data_addr,
+ sizeof(data_addr)) < 0) {
+ warn("connect");
+ goto bad;
+ }
+#ifdef IP_TOS
+ tos = IPTOS_THROUGHPUT;
+ if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&tos,
+ sizeof(int)) < 0)
+ warn("setsockopt TOS (ignored)");
+#endif
+ return (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) {
+ warn("socket");
+ if (tmpno)
+ sendport = 1;
+ return (1);
+ }
+ if (!sendport)
+ if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
+ sizeof(on)) < 0) {
+ warn("setsockopt (reuse address)");
+ goto bad;
+ }
+#ifdef IP_PORTRANGE
+ ports = restricted_data_ports ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT;
+ if (setsockopt(data, IPPROTO_IP, IP_PORTRANGE, (char *)&ports,
+ sizeof(ports)) < 0)
+ warn("setsockopt PORTRANGE (ignored)");
+#endif
+ 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)
+ const char *lmode;
+{
+ struct sockaddr_in from;
+ int s, fromlen, tos;
+
+ fromlen = sizeof(from);
+
+ 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
+psummary(notused)
+ int notused;
+{
+
+ if (bytes > 0)
+ ptransfer(1);
+}
+
+void
+psabort(notused)
+ int notused;
+{
+
+ alarmtimer(0);
+ 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[sizeof(ip->name) - 1] = '\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, sizeof(ip->nti) - 1);
+ (ip->nti)[sizeof(ip->nti) - 1] = '\0';
+ (void)strcpy(ntin, op->nti);
+ (void)strncpy(ip->nto, ntout, sizeof(ip->nto) - 1);
+ (ip->nto)[sizeof(ip->nto) - 1] = '\0';
+ (void)strcpy(ntout, op->nto);
+ ip->mapflg = mapflag;
+ mapflag = op->mapflg;
+ (void)strncpy(ip->mi, mapin, sizeof(ip->mi) - 1);
+ (ip->mi)[sizeof(ip->mi) - 1] = '\0';
+ (void)strcpy(mapin, op->mi);
+ (void)strncpy(ip->mo, mapout, sizeof(ip->mo) - 1);
+ (ip->mo)[sizeof(ip->mo) - 1] = '\0';
+ (void)strcpy(mapout, op->mo);
+ (void)signal(SIGINT, oldintr);
+ if (abrtflag) {
+ abrtflag = 0;
+ (*oldintr)(SIGINT);
+ }
+}
+
+void
+abortpt(notused)
+ int notused;
+{
+
+ alarmtimer(0);
+ putchar('\n');
+ (void)fflush(stdout);
+ ptabflg++;
+ mflag = 0;
+ abrtflag = 0;
+ longjmp(ptabort, 1);
+}
+
+void
+proxtrans(cmd, local, remote)
+ const 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) {
+ puts("proxy server does not support third party transfers.");
+ return;
+ }
+ pswitch(0);
+ if (!connected) {
+ puts("No primary connection.");
+ 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)
+ const 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) {
+ puts("runique: can't find unique file name.");
+ 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;
+
+ if (cout == NULL) {
+ warnx("Lost control connection for abort.");
+ if (ptabflg)
+ code = -1;
+ lostpeer();
+ return;
+ }
+ /*
+ * 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..3a7423f
--- /dev/null
+++ b/usr.bin/ftp/ftp_var.h
@@ -0,0 +1,169 @@
+/* $Id$ */
+/* $NetBSD: ftp_var.h,v 1.16 1997/04/14 09:09:23 lukem Exp $ */
+
+/*
+ * 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.4 (Berkeley) 10/9/94
+ */
+
+/*
+ * FTP global variables.
+ */
+
+#include <sys/param.h>
+#include <setjmp.h>
+#include <stringlist.h>
+
+#ifndef SMALL
+#include <histedit.h>
+#endif /* !SMALL */
+
+#include "extern.h"
+
+#define HASHBYTES 1024
+#define FTPBUFLEN MAXPATHLEN + 200
+
+#define STALLTIME 5 /* # of seconds of no xfer before "stalling" */
+
+#define FTP_PORT 21 /* default if getservbyname("ftp/tcp") fails */
+#define HTTP_PORT 80 /* default if getservbyname("http/tcp") fails */
+
+#define PAGER "less" /* default pager if $PAGER isn't set */
+
+/*
+ * Options and other state info.
+ */
+int trace; /* trace packets exchanged */
+int hash; /* print # for each buffer transferred */
+int mark; /* number of bytes between hashes */
+int sendport; /* use PORT cmd for each data connection */
+int verbose; /* print messages coming back from server */
+int connected; /* 1 = connected to server, -1 = logged in */
+int fromatty; /* input is from a terminal */
+int interactive; /* interactively prompt on m* cmds */
+int confirmrest; /* confirm rest of current m* cmd */
+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 preserve; /* preserve modification time on files */
+int progress; /* display transfer progress bar */
+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 */
+int anonftp; /* automatic anonymous login */
+int dirchange; /* remote directory changed by cd command */
+int ttywidth; /* width of tty */
+
+#ifndef SMALL
+int editing; /* command line editing enabled */
+EditLine *el; /* editline(3) status structure */
+History *hist; /* editline(3) history structure */
+char *cursor_pos; /* cursor position we're looking for */
+int cursor_argc; /* location of cursor in margv */
+int cursor_argo; /* offset of cursor in margv[cursor_argc] */
+#endif /* !SMALL */
+
+off_t bytes; /* current # of bytes read */
+off_t filesize; /* size of file being transferred */
+char *direction; /* direction transfer is occurring */
+
+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 */
+int ftpport; /* port number to use for ftp connections */
+int httpport; /* port number to use for http connections */
+
+jmp_buf toplevel; /* non-local goto stuff for cmd scanner */
+
+char line[FTPBUFLEN]; /* input line buffer */
+char *stringbase; /* current scan point in line buffer */
+char argbuf[FTPBUFLEN]; /* argument storage buffer */
+char *argbase; /* current storage point in arg buffer */
+StringList *marg_sl; /* stringlist containing margv */
+int margc; /* count of arguments on input line */
+#define margv (marg_sl->sl_str) /* 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 */
+#ifndef SMALL
+ char *c_complete; /* context sensitive completion list */
+#endif /* !SMALL */
+ 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..f94b951
--- /dev/null
+++ b/usr.bin/ftp/main.c
@@ -0,0 +1,651 @@
+/* $Id: main.c,v 1.14 1997/06/27 09:30:13 ache Exp $ */
+/* $NetBSD: main.c,v 1.22 1997/06/10 07:04:43 lukem Exp $ */
+
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94";
+#else
+static char rcsid[] = "$Id: main.c,v 1.14 1997/06/27 09:30:13 ache Exp $";
+#endif
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Command Interface.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <err.h>
+#include <locale.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ftp_var.h"
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct servent *sp;
+ int ch, top, port, rval;
+ struct passwd *pw = NULL;
+ char *cp, homedir[MAXPATHLEN];
+ int dumbterm;
+
+ (void) setlocale(LC_ALL, "");
+
+ sp = getservbyname("ftp", "tcp");
+ if (sp == 0)
+ ftpport = htons(FTP_PORT); /* good fallback */
+ else
+ ftpport = sp->s_port;
+ sp = getservbyname("http", "tcp");
+ if (sp == 0)
+ httpport = htons(HTTP_PORT); /* good fallback */
+ else
+ httpport = sp->s_port;
+ doglob = 1;
+ interactive = 1;
+ autologin = 1;
+ passivemode = 0;
+ restricted_data_ports = 1;
+ preserve = 1;
+ verbose = 0;
+ progress = 0;
+#ifndef SMALL
+ editing = 0;
+ el = NULL;
+ hist = NULL;
+#endif
+ mark = HASHBYTES;
+ marg_sl = sl_init();
+
+ cp = strrchr(argv[0], '/');
+ cp = (cp == NULL) ? argv[0] : cp + 1;
+ if (getenv("FTP_PASSIVE_MODE") || strcmp(cp, "pftp") == 0)
+ passivemode = 1;
+
+ cp = getenv("TERM");
+ if (cp == NULL || strcmp(cp, "dumb") == 0)
+ dumbterm = 1;
+ else
+ dumbterm = 0;
+ fromatty = isatty(fileno(stdin));
+ if (fromatty) {
+ verbose = 1; /* verbose if from a tty */
+#ifndef SMALL
+ if (! dumbterm)
+ editing = 1; /* editing mode on if tty is usable */
+#endif
+ }
+ if (isatty(fileno(stdout)) && !dumbterm)
+ progress = 1; /* progress bar on if tty is usable */
+
+ while ((ch = getopt(argc, argv, "adeginpP:tvVU")) != -1) {
+ switch (ch) {
+ case 'a':
+ anonftp = 1;
+ break;
+
+ case 'd':
+ options |= SO_DEBUG;
+ debug++;
+ break;
+
+ case 'e':
+#ifndef SMALL
+ editing = 0;
+#endif
+ break;
+
+ case 'g':
+ doglob = 0;
+ break;
+
+ case 'i':
+ interactive = 0;
+ break;
+
+ case 'n':
+ autologin = 0;
+ break;
+
+ case 'p':
+ passivemode = 1;
+ break;
+
+ case 'P':
+ port = atoi(optarg);
+ if (port <= 0)
+ warnx("bad port number: %s (ignored)", optarg);
+ else
+ ftpport = htons(port);
+ break;
+
+ case 't':
+ trace = 1;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'V':
+ verbose = 0;
+ break;
+
+ case 'U':
+ restricted_data_ports = 0;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ 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);
+ }
+
+ setttywidth(0);
+ (void)signal(SIGWINCH, setttywidth);
+
+ if (argc > 0) {
+ if (strchr(argv[0], ':') != NULL) {
+ anonftp = 1; /* Handle "automatic" transfers. */
+ rval = auto_fetch(argc, argv);
+ if (rval >= 0) /* -1 == connected and cd-ed */
+ exit(rval);
+ } else {
+ char *xargv[5];
+
+ if (setjmp(toplevel))
+ exit(0);
+ (void)signal(SIGINT, (sig_t)intr);
+ (void)signal(SIGPIPE, (sig_t)lostpeer);
+ xargv[0] = __progname;
+ xargv[1] = argv[0];
+ xargv[2] = argv[1];
+ xargv[3] = argv[2];
+ xargv[4] = NULL;
+ setpeer(argc+1, xargv);
+ }
+ }
+#ifndef SMALL
+ controlediting();
+#endif /* !SMALL */
+ top = setjmp(toplevel) == 0;
+ if (top) {
+ (void)signal(SIGINT, (sig_t)intr);
+ (void)signal(SIGPIPE, (sig_t)lostpeer);
+ }
+ for (;;) {
+ cmdscanner(top);
+ top = 1;
+ }
+}
+
+void
+intr()
+{
+
+ alarmtimer(0);
+ longjmp(toplevel, 1);
+}
+
+void
+lostpeer()
+{
+
+ alarmtimer(0);
+ 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);
+}
+
+/*
+ * Generate a prompt
+ */
+char *
+prompt()
+{
+ return ("ftp> ");
+}
+
+/*
+ * Command parser.
+ */
+void
+cmdscanner(top)
+ int top;
+{
+ struct cmd *c;
+ int num;
+
+ if (!top
+#ifndef SMALL
+ && !editing
+#endif /* !SMALL */
+ )
+ (void)putchar('\n');
+ for (;;) {
+#ifndef SMALL
+ if (!editing) {
+#endif /* !SMALL */
+ if (fromatty) {
+ fputs(prompt(), stdout);
+ (void)fflush(stdout);
+ }
+ if (fgets(line, sizeof(line), stdin) == NULL)
+ quit(0, 0);
+ num = strlen(line);
+ if (num == 0)
+ break;
+ if (line[--num] == '\n') {
+ if (num == 0)
+ break;
+ line[num] = '\0';
+ } else if (num == sizeof(line) - 2) {
+ puts("sorry, input line too long.");
+ while ((num = getchar()) != '\n' && num != EOF)
+ /* void */;
+ break;
+ } /* else it was a line without a newline */
+#ifndef SMALL
+ } else {
+ const char *buf;
+ cursor_pos = NULL;
+
+ if ((buf = el_gets(el, &num)) == NULL || num == 0)
+ quit(0, 0);
+ if (line[--num] == '\n') {
+ if (num == 0)
+ break;
+ } else if (num >= sizeof(line)) {
+ puts("sorry, input line too long.");
+ break;
+ }
+ memcpy(line, buf, num);
+ line[num] = '\0';
+ history(hist, H_ENTER, buf);
+ }
+#endif /* !SMALL */
+
+ makeargv();
+ if (margc == 0)
+ continue;
+#if 0 && !defined(SMALL) /* XXX: don't want el_parse */
+ /*
+ * el_parse returns -1 to signal that it's not been handled
+ * internally.
+ */
+ if (el_parse(el, margc, margv) != -1)
+ continue;
+#endif /* !SMALL */
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ puts("?Ambiguous command.");
+ continue;
+ }
+ if (c == 0) {
+ puts("?Invalid command.");
+ continue;
+ }
+ if (c->c_conn && !connected) {
+ puts("Not connected.");
+ continue;
+ }
+ confirmrest = 0;
+ (*c->c_handler)(margc, margv);
+ if (bell && c->c_bell)
+ (void)putchar('\007');
+ if (c->c_handler != help)
+ break;
+ }
+ (void)signal(SIGINT, (sig_t)intr);
+ (void)signal(SIGPIPE, (sig_t)lostpeer);
+}
+
+struct cmd *
+getcmd(name)
+ const char *name;
+{
+ const char *p, *q;
+ struct cmd *c, *found;
+ int nmatches, longest;
+
+ if (name == NULL)
+ return (0);
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; (p = c->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.
+ */
+
+int slrflag;
+
+void
+makeargv()
+{
+ char *argp;
+
+ stringbase = line; /* scan from first of buffer */
+ argbase = argbuf; /* store from first of buffer */
+ slrflag = 0;
+ marg_sl->sl_cur = 0; /* reset to start of marg_sl */
+ for (margc = 0; ; margc++) {
+ argp = slurpstring();
+ sl_add(marg_sl, argp);
+ if (argp == NULL)
+ break;
+ }
+#ifndef SMALL
+ if (cursor_pos == line) {
+ cursor_argc = 0;
+ cursor_argo = 0;
+ } else if (cursor_pos != NULL) {
+ cursor_argc = margc;
+ cursor_argo = strlen(margv[margc-1]);
+ }
+#endif /* !SMALL */
+}
+
+#ifdef SMALL
+#define INC_CHKCURSOR(x) (x)++
+#else /* !SMALL */
+#define INC_CHKCURSOR(x) { (x)++ ; \
+ if (x == cursor_pos) { \
+ cursor_argc = margc; \
+ cursor_argo = ap-argbase; \
+ cursor_pos = NULL; \
+ } }
+
+#endif /* !SMALL */
+
+/*
+ * 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++;
+ INC_CHKCURSOR(stringbase);
+ return ((*sb == '!') ? "!" : "$");
+ /* NOTREACHED */
+ case 1:
+ slrflag++;
+ altarg = stringbase;
+ break;
+ default:
+ break;
+ }
+ }
+
+S0:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ case ' ':
+ case '\t':
+ INC_CHKCURSOR(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 '\\':
+ INC_CHKCURSOR(sb);
+ goto S2; /* slurp next character */
+
+ case '"':
+ INC_CHKCURSOR(sb);
+ goto S3; /* slurp quoted string */
+
+ default:
+ *ap = *sb; /* add character to token */
+ ap++;
+ INC_CHKCURSOR(sb);
+ got_one = 1;
+ goto S1;
+ }
+
+S2:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ default:
+ *ap = *sb;
+ ap++;
+ INC_CHKCURSOR(sb);
+ got_one = 1;
+ goto S1;
+ }
+
+S3:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ case '"':
+ INC_CHKCURSOR(sb);
+ goto S1;
+
+ default:
+ *ap = *sb;
+ ap++;
+ INC_CHKCURSOR(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);
+}
+
+/*
+ * 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) {
+ StringList *buf;
+
+ buf = sl_init();
+ printf("%sommands may be abbreviated. Commands are:\n\n",
+ proxy ? "Proxy c" : "C");
+ for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
+ if (c->c_name && (!proxy || c->c_proxy))
+ sl_add(buf, c->c_name);
+ list_vertical(buf);
+ sl_free(buf, 0);
+ return;
+ }
+
+#define HELPINDENT ((int) sizeof("disconnect"))
+
+ 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);
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: %s [-adeginptvV] [host [port]]\n"
+ " %s host:path[/]\n"
+ " %s ftp://host[:port]/path[/]\n"
+ " %s http://host[:port]/file\n",
+ __progname, __progname, __progname, __progname);
+ exit(1);
+}
diff --git a/usr.bin/ftp/pathnames.h b/usr.bin/ftp/pathnames.h
new file mode 100644
index 0000000..c764add
--- /dev/null
+++ b/usr.bin/ftp/pathnames.h
@@ -0,0 +1,41 @@
+/* $Id$ */
+/* $NetBSD: pathnames.h,v 1.7 1997/01/09 20:19:40 tls Exp $ */
+
+/*
+ * 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 TMPFILE "ftpXXXXXX"
diff --git a/usr.bin/ftp/ruserpass.c b/usr.bin/ftp/ruserpass.c
new file mode 100644
index 0000000..35302a0
--- /dev/null
+++ b/usr.bin/ftp/ruserpass.c
@@ -0,0 +1,302 @@
+/* $Id: ruserpass.c,v 1.5 1997/06/25 08:56:45 msmith Exp $ */
+/* $NetBSD: ruserpass.c,v 1.13 1997/04/01 14:20:34 mrg Exp $ */
+
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)ruserpass.c 8.4 (Berkeley) 4/27/95";
+#else
+static char rcsid[] = "$Id: ruserpass.c,v 1.5 1997/06/25 08:56:45 msmith Exp $";
+#endif
+#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)
+ const char *host;
+ char **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 = ".";
+ if (strlen(hdir) + sizeof(".netrc") < sizeof(buf)) {
+ (void)snprintf(buf, sizeof buf, "%s/.netrc", hdir);
+ } else {
+ warnx("%s/.netrc: %s", hdir, strerror(ENAMETOOLONG));
+ return (0);
+ }
+ 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)
+ if (c != ' ' && c != '\t')
+ break;
+ if (c == EOF || c == '\n') {
+ puts("Missing macdef name argument.");
+ goto bad;
+ }
+ if (macnum == 16) {
+ puts(
+"Limit of 16 macros have already been defined.");
+ goto bad;
+ }
+ tmp = macros[macnum].mac_name;
+ *tmp++ = c;
+ for (i=0; i < 8 && (c=getc(cfile)) != EOF &&
+ (!isascii(c) || !isspace(c)); ++i) {
+ *tmp++ = c;
+ }
+ if (c == EOF) {
+ puts(
+"Macro definition missing null line terminator.");
+ goto bad;
+ }
+ *tmp = '\0';
+ if (c != '\n') {
+ while ((c=getc(cfile)) != EOF && c != '\n');
+ }
+ if (c == EOF) {
+ puts(
+"Macro definition missing null line terminator.");
+ 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) {
+ puts(
+"Macro definition missing null line terminator.");
+ 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) {
+ puts("4K macro buffer exceeded.");
+ 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/ftp/util.c b/usr.bin/ftp/util.c
new file mode 100644
index 0000000..30b48c7
--- /dev/null
+++ b/usr.bin/ftp/util.c
@@ -0,0 +1,779 @@
+/* $Id: util.c,v 1.1 1997/06/25 08:56:46 msmith Exp $ */
+/* $NetBSD: util.c,v 1.9 1997/06/10 22:00:01 lukem Exp $ */
+
+/*
+ * 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 rcsid[] = "$Id: util.c,v 1.1 1997/06/25 08:56:46 msmith Exp $";
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Misc support routines
+ */
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <arpa/ftp.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "ftp_var.h"
+#include "pathnames.h"
+
+/*
+ * 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 = ftpport;
+ 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], NULL, NULL);
+
+ overbose = verbose;
+ if (debug == 0)
+ verbose = -1;
+ if (command("SYST") == COMPLETE && overbose) {
+ char *cp, c;
+ c = 0;
+ 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))
+ puts(
+"Remember to set tenex mode when transferring binary files from this machine.");
+ }
+ verbose = overbose;
+ }
+}
+
+
+/*
+ * login to remote host, using given username & password if supplied
+ */
+int
+login(host, user, pass)
+ const char *host;
+ char *user, *pass;
+{
+ char tmp[80];
+ char *acct;
+ char anonpass[MAXLOGNAME + 1 + MAXHOSTNAMELEN]; /* "user@hostname" */
+ char hostname[MAXHOSTNAMELEN];
+ int n, aflag = 0;
+
+ acct = NULL;
+ if (user == NULL) {
+ if (ruserpass(host, &user, &pass, &acct) < 0) {
+ code = -1;
+ return (0);
+ }
+ }
+
+ /*
+ * Set up arguments for an anonymous FTP session, if necessary.
+ */
+ if ((user == NULL || pass == NULL) && anonftp) {
+ memset(anonpass, 0, sizeof(anonpass));
+ memset(hostname, 0, sizeof(hostname));
+
+ /*
+ * Set up anonymous login password.
+ */
+ user = getlogin();
+ gethostname(hostname, MAXHOSTNAMELEN);
+#ifndef DONT_CHEAT_ANONPASS
+ /*
+ * Every anonymous FTP server I've encountered
+ * will accept the string "username@", and will
+ * append the hostname itself. We do this by default
+ * since many servers are picky about not having
+ * a FQDN in the anonymous password. - thorpej@netbsd.org
+ */
+ snprintf(anonpass, sizeof(anonpass) - 1, "%s@",
+ user);
+#else
+ snprintf(anonpass, sizeof(anonpass) - 1, "%s@%s",
+ user, hp->h_name);
+#endif
+ pass = anonpass;
+ user = "anonymous"; /* as per RFC 1635 */
+ }
+
+ 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++;
+ if (acct == NULL)
+ acct = getpass("Account:");
+ n = command("ACCT %s", acct);
+ }
+ if ((n != COMPLETE) ||
+ (!aflag && acct != NULL && command("ACCT %s", acct) != COMPLETE)) {
+ warnx("Login failed.");
+ return (0);
+ }
+ if (proxy)
+ return (1);
+ connected = -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);
+}
+
+/*
+ * `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;
+ const char *prompt;
+{
+ int len = strlen(line), ret;
+
+ if (len >= sizeof(line) - 3) {
+ puts("sorry, arguments too long.");
+ 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);
+}
+
+/*
+ * glob files given in argv[] from the remote server.
+ * if errbuf isn't NULL, store error messages there instead
+ * of writing to the screen.
+ */
+char *
+remglob(argv, doswitch, errbuf)
+ char *argv[];
+ int doswitch;
+ char **errbuf;
+{
+ char temp[MAXPATHLEN];
+ static char buf[MAXPATHLEN];
+ static FILE *ftemp = NULL;
+ static char **args;
+ int oldverbose, oldhash, fd;
+ 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)snprintf(temp, sizeof(temp), "%s%s", _PATH_TMP, TMPFILE);
+ if ((fd = mkstemp(temp)) < 0) {
+ warn("unable to create temporary file %s", temp);
+ return (NULL);
+ }
+ close(fd);
+ oldverbose = verbose;
+ verbose = (errbuf != NULL) ? -1 : 0;
+ oldhash = hash;
+ hash = 0;
+ if (doswitch)
+ pswitch(!proxy);
+ for (mode = "w"; *++argv != NULL; mode = "a")
+ recvrequest("NLST", temp, *argv, mode, 0);
+ if ((code / 100) != COMPLETE) {
+ if (errbuf != NULL)
+ *errbuf = reply_string;
+ }
+ if (doswitch)
+ pswitch(!proxy);
+ verbose = oldverbose;
+ hash = oldhash;
+ ftemp = fopen(temp, "r");
+ (void)unlink(temp);
+ if (ftemp == NULL) {
+ if (errbuf == NULL)
+ puts("can't find list of remote files, oops.");
+ else
+ *errbuf =
+ "can't find list of remote files, oops.";
+ 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);
+}
+
+int
+confirm(cmd, file)
+ const char *cmd, *file;
+{
+ char line[BUFSIZ];
+
+ if (!interactive || confirmrest)
+ return (1);
+ printf("%s %s? ", cmd, file);
+ (void)fflush(stdout);
+ if (fgets(line, sizeof(line), stdin) == NULL)
+ return (0);
+ switch (tolower((unsigned char)*line)) {
+ case 'n':
+ return (0);
+ case 'p':
+ interactive = 0;
+ puts("Interactive mode: off.");
+ break;
+ case 'a':
+ confirmrest = 1;
+ printf("Prompting off for duration of %s.\n", cmd);
+ break;
+ }
+ return (1);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * determine size of remote file
+ */
+off_t
+remotesize(file, noisy)
+ const char *file;
+ int noisy;
+{
+ int overbose;
+ off_t size;
+
+ overbose = verbose;
+ size = -1;
+ if (debug == 0)
+ verbose = -1;
+ if (command("SIZE %s", file) == COMPLETE)
+ sscanf(reply_string, "%*s %qd", &size);
+ else if (noisy && debug == 0)
+ puts(reply_string);
+ verbose = overbose;
+ return (size);
+}
+
+/*
+ * determine last modification time (in GMT) of remote file
+ */
+time_t
+remotemodtime(file, noisy)
+ const char *file;
+ int noisy;
+{
+ int overbose;
+ time_t rtime;
+
+ overbose = verbose;
+ rtime = -1;
+ if (debug == 0)
+ verbose = -1;
+ if (command("MDTM %s", file) == COMPLETE) {
+ struct tm timebuf;
+ int yy, mo, day, hour, min, sec;
+ sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
+ &day, &hour, &min, &sec);
+ memset(&timebuf, 0, sizeof(timebuf));
+ timebuf.tm_sec = sec;
+ timebuf.tm_min = min;
+ timebuf.tm_hour = hour;
+ timebuf.tm_mday = day;
+ timebuf.tm_mon = mo - 1;
+ timebuf.tm_year = yy - 1900;
+ timebuf.tm_isdst = -1;
+ rtime = mktime(&timebuf);
+ if (rtime == -1 && (noisy || debug != 0))
+ printf("Can't convert %s to a time.\n", reply_string);
+ else
+ rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */
+ } else if (noisy && debug == 0)
+ puts(reply_string);
+ verbose = overbose;
+ return (rtime);
+}
+
+void
+updateprogressmeter()
+{
+ static pid_t pgrp = -1;
+ int ctty_pgrp;
+
+ if (pgrp == -1)
+ pgrp = getpgrp();
+
+ /*
+ * print progress bar only if we are foreground process.
+ */
+ if (ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 &&
+ ctty_pgrp == (int)pgrp)
+ progressmeter(0);
+}
+
+/*
+ * Display a transfer progress bar if progress is non-zero.
+ * SIGALRM is hijacked for use by this function.
+ * - Before the transfer, set filesize to size of file (or -1 if unknown),
+ * and call with flag = -1. This starts the once per second timer,
+ * and a call to updateprogressmeter() upon SIGALRM.
+ * - During the transfer, updateprogressmeter will call progressmeter
+ * with flag = 0
+ * - After the transfer, call with flag = 1
+ */
+static struct timeval start;
+
+void
+progressmeter(flag)
+ int flag;
+{
+ /*
+ * List of order of magnitude prefixes.
+ * The last is `P', as 2^64 = 16384 Petabytes
+ */
+ static const char prefixes[] = " KMGTP";
+
+ static struct timeval lastupdate;
+ static off_t lastsize;
+ struct timeval now, td, wait;
+ off_t cursize, abbrevsize;
+ double elapsed;
+ int ratio, barlength, i, remaining;
+ char buf[256];
+
+ if (flag == -1) {
+ (void)gettimeofday(&start, (struct timezone *)0);
+ lastupdate = start;
+ lastsize = restart_point;
+ }
+ (void)gettimeofday(&now, (struct timezone *)0);
+ if (!progress || filesize <= 0)
+ return;
+ cursize = bytes + restart_point;
+
+ ratio = cursize * 100 / filesize;
+ ratio = MAX(ratio, 0);
+ ratio = MIN(ratio, 100);
+ snprintf(buf, sizeof(buf), "\r%3d%% ", ratio);
+
+ barlength = ttywidth - 30;
+ if (barlength > 0) {
+ i = barlength * ratio / 100;
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ "|%.*s%*s|", i,
+"*****************************************************************************"
+"*****************************************************************************",
+ barlength - i, "");
+ }
+
+ i = 0;
+ abbrevsize = cursize;
+ while (abbrevsize >= 100000 && i < sizeof(prefixes)) {
+ i++;
+ abbrevsize >>= 10;
+ }
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ " %5qd %c%c ", abbrevsize, prefixes[i],
+ prefixes[i] == ' ' ? ' ' : 'B');
+
+ timersub(&now, &lastupdate, &wait);
+ if (cursize > lastsize) {
+ lastupdate = now;
+ lastsize = cursize;
+ if (wait.tv_sec >= STALLTIME) { /* fudge out stalled time */
+ start.tv_sec += wait.tv_sec;
+ start.tv_usec += wait.tv_usec;
+ }
+ wait.tv_sec = 0;
+ }
+
+ timersub(&now, &start, &td);
+ elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
+
+ if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ " --:-- ETA");
+ } else if (wait.tv_sec >= STALLTIME) {
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ " - stalled -");
+ } else {
+ remaining = (int)((filesize - restart_point) /
+ (bytes / elapsed) - elapsed);
+ i = remaining / 3600;
+ if (i)
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ "%2d:", i);
+ else
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ " ");
+ i = remaining % 3600;
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ "%02d:%02d ETA", i / 60, i % 60);
+ }
+ (void)write(STDOUT_FILENO, buf, strlen(buf));
+
+ if (flag == -1) {
+ (void)signal(SIGALRM, updateprogressmeter);
+ alarmtimer(1); /* set alarm timer for 1 Hz */
+ } else if (flag == 1) {
+ alarmtimer(0);
+ (void)putchar('\n');
+ }
+ fflush(stdout);
+}
+
+/*
+ * Display transfer statistics.
+ * Requires start to be initialised by progressmeter(-1),
+ * direction to be defined by xfer routines, and filesize and bytes
+ * to be updated by xfer routines
+ * If siginfo is nonzero, an ETA is displayed, and the output goes to STDERR
+ * instead of STDOUT.
+ */
+void
+ptransfer(siginfo)
+ int siginfo;
+{
+ struct timeval now, td;
+ double elapsed;
+ off_t bs;
+ int meg, remaining, hh;
+ char buf[100];
+
+ if (!verbose && !siginfo)
+ return;
+
+ (void)gettimeofday(&now, (struct timezone *)0);
+ timersub(&now, &start, &td);
+ elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
+ bs = bytes / (elapsed == 0.0 ? 1 : elapsed);
+ meg = 0;
+ if (bs > (1024 * 1024))
+ meg = 1;
+ (void)snprintf(buf, sizeof(buf),
+ "%qd byte%s %s in %.2f seconds (%.2f %sB/s)\n",
+ bytes, bytes == 1 ? "" : "s", direction, elapsed,
+ bs / (1024.0 * (meg ? 1024.0 : 1.0)), meg ? "M" : "K");
+ if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0
+ && bytes + restart_point <= filesize) {
+ remaining = (int)((filesize - restart_point) /
+ (bytes / elapsed) - elapsed);
+ hh = remaining / 3600;
+ remaining %= 3600;
+ /* "buf+len(buf) -1" to overwrite \n */
+ snprintf(buf + strlen(buf) - 1, sizeof(buf) - strlen(buf),
+ " ETA: %02d:%02d:%02d\n", hh, remaining / 60,
+ remaining % 60);
+ }
+ (void)write(siginfo ? STDERR_FILENO : STDOUT_FILENO, buf, strlen(buf));
+}
+
+/*
+ * List words in stringlist, vertically arranged
+ */
+void
+list_vertical(sl)
+ StringList *sl;
+{
+ int i, j, w;
+ int columns, width, lines, items;
+ char *p;
+
+ width = items = 0;
+
+ for (i = 0 ; i < sl->sl_cur ; i++) {
+ w = strlen(sl->sl_str[i]);
+ if (w > width)
+ width = w;
+ }
+ width = (width + 8) &~ 7;
+
+ columns = ttywidth / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (sl->sl_cur + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ p = sl->sl_str[j * lines + i];
+ if (p)
+ fputs(p, stdout);
+ if (j * lines + i + lines >= sl->sl_cur) {
+ putchar('\n');
+ break;
+ }
+ w = strlen(p);
+ while (w < width) {
+ w = (w + 8) &~ 7;
+ (void)putchar('\t');
+ }
+ }
+ }
+}
+
+/*
+ * Update the global ttywidth value, using TIOCGWINSZ.
+ */
+void
+setttywidth(a)
+ int a;
+{
+ struct winsize winsize;
+
+ if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1)
+ ttywidth = winsize.ws_col;
+ else
+ ttywidth = 80;
+}
+
+/*
+ * Set the SIGALRM interval timer for wait seconds, 0 to disable.
+ */
+void
+alarmtimer(wait)
+ int wait;
+{
+ struct itimerval itv;
+
+ itv.it_value.tv_sec = wait;
+ itv.it_value.tv_usec = 0;
+ itv.it_interval = itv.it_value;
+ setitimer(ITIMER_REAL, &itv, NULL);
+}
+
+/*
+ * Setup or cleanup EditLine structures
+ */
+#ifndef SMALL
+void
+controlediting()
+{
+ if (editing && el == NULL && hist == NULL) {
+ el = el_init(__progname, stdin, stdout); /* init editline */
+ hist = history_init(); /* init the builtin history */
+ history(hist, H_EVENT, 100); /* remember 100 events */
+ el_set(el, EL_HIST, history, hist); /* use history */
+
+ el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */
+ el_set(el, EL_PROMPT, prompt); /* set the prompt function */
+
+ /* add local file completion, bind to TAB */
+ el_set(el, EL_ADDFN, "ftp-complete",
+ "Context sensitive argument completion",
+ complete);
+ el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
+
+ el_source(el, NULL); /* read ~/.editrc */
+ el_set(el, EL_SIGNAL, 1);
+ } else if (!editing) {
+ if (hist) {
+ history_end(hist);
+ hist = NULL;
+ }
+ if (el) {
+ el_end(el);
+ el = NULL;
+ }
+ }
+}
+#endif /* !SMALL */
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..7ecbe1c
--- /dev/null
+++ b/usr.bin/gcore/aoutcore.c
@@ -0,0 +1,314 @@
+/*-
+ * 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 *, const 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")) != -1) {
+ 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[PAGE_SIZE];
+
+ delta = data_offset - addr;
+ while (--npage >= 0) {
+ cc = kvm_uread(kd, p, addr, buffer, PAGE_SIZE);
+ if (cc != PAGE_SIZE) {
+ /* 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, PAGE_SIZE);
+ if (cc != PAGE_SIZE)
+ err(1, "write data segment: %s",
+ cc > 0 ? strerror(EIO) : strerror(errno));
+ addr += PAGE_SIZE;
+ }
+}
+
+void
+userdump(fd, p, addr, npage)
+ register int fd;
+ struct proc *p;
+ register u_long addr;
+ register int npage;
+{
+ register int cc;
+ char buffer[PAGE_SIZE];
+
+ while (--npage >= 0) {
+ cc = kvm_uread(kd, p, addr, buffer, PAGE_SIZE);
+ if (cc != PAGE_SIZE)
+ /* Could be an untouched fill-with-zero page. */
+ bzero(buffer, PAGE_SIZE);
+ cc = write(fd, buffer, PAGE_SIZE);
+ if (cc != PAGE_SIZE)
+ err(1, "write stack segment: %s",
+ cc > 0 ? strerror(EIO) : strerror(errno));
+ addr += PAGE_SIZE;
+ }
+}
+
+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..ce8fc68
--- /dev/null
+++ b/usr.bin/gcore/gcore.1
@@ -0,0 +1,95 @@
+.\" 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
+.Bx 4.2 .
+.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
+.Bx 4.2
+version.
+In particular,
+.Bx 4.4
+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..7ecbe1c
--- /dev/null
+++ b/usr.bin/gcore/gcore.c
@@ -0,0 +1,314 @@
+/*-
+ * 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 *, const 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")) != -1) {
+ 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[PAGE_SIZE];
+
+ delta = data_offset - addr;
+ while (--npage >= 0) {
+ cc = kvm_uread(kd, p, addr, buffer, PAGE_SIZE);
+ if (cc != PAGE_SIZE) {
+ /* 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, PAGE_SIZE);
+ if (cc != PAGE_SIZE)
+ err(1, "write data segment: %s",
+ cc > 0 ? strerror(EIO) : strerror(errno));
+ addr += PAGE_SIZE;
+ }
+}
+
+void
+userdump(fd, p, addr, npage)
+ register int fd;
+ struct proc *p;
+ register u_long addr;
+ register int npage;
+{
+ register int cc;
+ char buffer[PAGE_SIZE];
+
+ while (--npage >= 0) {
+ cc = kvm_uread(kd, p, addr, buffer, PAGE_SIZE);
+ if (cc != PAGE_SIZE)
+ /* Could be an untouched fill-with-zero page. */
+ bzero(buffer, PAGE_SIZE);
+ cc = write(fd, buffer, PAGE_SIZE);
+ if (cc != PAGE_SIZE)
+ err(1, "write stack segment: %s",
+ cc > 0 ? strerror(EIO) : strerror(errno));
+ addr += PAGE_SIZE;
+ }
+}
+
+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..3b42987
--- /dev/null
+++ b/usr.bin/gencat/genlib.c
@@ -0,0 +1,896 @@
+/* -*-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;
+ 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..ebf1c65
--- /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 Ar 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.
+.Ar 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
+.Ql \-\-
+is used to delimit the end of the options.
+.Nm Getopt
+will place
+.Ql \-\-
+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
+.Ql \-
+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
+.Fl a
+and
+.Fl b ,
+and the option
+.Fl 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
+.Ar 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 embedded 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..060e2c9
--- /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])) != -1)
+ 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/global/HISTORY b/usr.bin/global/HISTORY
new file mode 100644
index 0000000..de9eb3c
--- /dev/null
+++ b/usr.bin/global/HISTORY
@@ -0,0 +1,161 @@
+GLOBAL history
+--------------
+
+version 1.0 initial version [21-Apr-96]
+
+version 1.1 only bugfix [2-May-96]
+
+ [fixed bug]
+ global - makes corrupted path name by conversion error.
+ - description of exit code in online manual is mistaken.
+ btreeop - cannot treat a long line over BUFSIZ. With the result
+ that gtags fails.
+ extended ctags - when using -r option, considers a reserved word
+ to be a function name. With the result that GRTAGS becomes
+ too large.
+
+version 1.2 mainly unification of files [7-Jun-96]
+
+ [changed]
+ extended ctags
+ - change to use bsearch(3) for searching reserved words.
+ - change to consider '__P' as a reserved word.
+ - unify the original patch (included in version1.0) and
+ the bugfix patch (included in version1.1).
+ extended vi
+ - unify the VI entended patch.
+
+version 1.3 support of GTAGSLIBPATH [28-Jul-96]
+
+ [changed]
+ global - search in not only a source tree but also library paths
+ specified by environment variable GTAGSLIBPATH.
+ extended ctags
+ - change print format a little when -x option
+
+version 1.4 support of yacc source file [26-Oct-96]
+
+ [changed]
+ extended ctags
+ - support of yacc source file.
+ - this version of ctags is NOT COMPATIBLE with original one.
+ * search all part of a yacc file for C functions.
+ (original only 3rd part)
+ * no longer considers a yacc rule to be an object.
+
+version 1.5 hypertext generator [12-Dec-96]
+
+ [fixed bug]
+ gtags - doesn't skip 'y.tab.c'.(we should treat only *.y)
+ extended vi
+ - free stack memory. free(3) in FreeBSD 2.1.5R seems to
+ show a warning message when receive stack address.
+ So it spoils the screen.
+ [added]
+ htags - new command (hypertext generator of C source file)
+ gtags - error check code added.
+
+version 1.6 support of 1.76 nvi and reconstruction for other OS [21-Jan-97]
+
+ [changed]
+ htags - cease using <BLOCKQUOTE> because lynx doesn't understand it.
+ global,htags
+ - replace 'sort -u' with 'sort | uniq' for compatibility reason.
+ [fixed bug]
+ global - makes illegal path name when using GTAGSLIBPATH.
+ gtags - error message mistaken.
+ [added]
+ nvi-1.76.diff
+ - patch for nvi 1.76 to make extended vi.
+ gctags - this is the same with extended ctags in older version.
+ now GLOBAL includes BSD ctags in it. so no longer has
+ patch file (ctags.diff).
+ (I brought original ctags from FreeBSD 2.1.5R)
+ global - -a option added.
+
+version 1.7 make suitable for large project (mainly FreeBSD kernel) [17-Feb-97]
+
+ [changed]
+ htags - htags no longer makes frame.html. Index.html offers frame
+ function too.
+ - works MUCH faster than previous version.
+ [fixed bug]
+ htags - generate path name including '&param'. (It means special
+ charactor in hypertertext.)
+ So, changed internal separator from '&' to '|'.
+ [added]
+ gtags - -s option added. If specified, gtags extract ENTRY() and
+ ALTENTRY() from *.[sS] files.
+ htags - supports one level nested index. It's always valid for file
+ index when directories found, and valid for function index
+ only when -a option specified.
+ - -v and -t option added.
+ systags.sh
+ - script to make all tags (GTAGS,GRTAGS,HTML) for kernel source.
+ (it is only for FreeBSD and Linux)
+
+version 1.8 fix some bugs, add options and make more portable [5-Apr-97]
+
+ [changed]
+ gctags, btreeop, Makefile
+ - modify for generic UNIX (Linux, Solaris)
+ - modify to quiet compiler.
+
+ Many thanks to
+ A.E. Brouwer
+ Oleg Checkulaev
+ Emile Heyns
+
+ gctags - modify to treat '\r' at the end of line.
+ htags - changed internal separator from '|' to ' '.
+ because some OS cannot treat '|' in a path.
+ [fixed bug]
+ htags - regard a part of path in #include as a reserved word.
+ - generate illegal links when same tags exist in a line.
+ - insufficient comment detection.
+ gctags - regard function name as a definition in extern statement.
+ - skip macro(no argment) body when -r specified.
+ gctags, htags
+ - regard 'entry' as a reserved word.
+ [added]
+ global - -c (complete) option added.
+ htags - -d tagdir option added.
+ - -w option added.
+
+version 1.81 make global to understand 'obj' directory and bug fix. [14-Apr-97]
+
+ [fixed bug]
+ extended vi
+ - doesn't hand '\' in a pattern to global.
+ [changed]
+ global
+ - can find tag file in obj directory.
+
+version 1.9 fix some bugs, add options and support of 1.79 nvi [21-Apr-97]
+
+ [fixed bug]
+ htags - doesn't keep the code formatted correctly.
+ So, changed to convert tabs in source files into spaces.
+ (expand(1) needed)
+
+ Thanks to Andy Newman.
+
+ - cannot print error message correctly.
+ [changed]
+ gctags, btreeop
+ - modify for SunOS 4.1.3.
+
+ Thanks to Yoshiharu Ito.
+
+ [added]
+ btreeop - META record and 'format version record' is available.
+ (It will be used in the furture.)
+ - -A and -D option added. (It will be used in the future.)
+ htags - use temporary directory specified by TMPDIR environment
+ variable.
+ - -l option added.
+
+ Thanks to Jeff Trawick. This option is his idea.
+
+ nvi-1.79.diff
+ - patch for nvi 1.79 to make extended vi.
diff --git a/usr.bin/global/MANIFEST b/usr.bin/global/MANIFEST
new file mode 100644
index 0000000..11e3b9e
--- /dev/null
+++ b/usr.bin/global/MANIFEST
@@ -0,0 +1,17 @@
+HISTORY Histroy of GLOBAL.
+INSTALL Installation method
+MANIFEST This file.
+Makefile Makefile for BSD.
+Makefile.inc A part of Makefile for BSD.
+Makefile.generic Makefile for generic UNIX(including BSD).
+README Readme (introduction and usage).
+VERSION Version number.
+btreeop/ Btreeop command directory.
+gctags/ Gctags command directory (extended ctags).
+global/ Global command directory.
+gtags/ Gtags command directory.
+htags/ Htags command directory.
+systags/ Script for kernel.
+nvi-1.34.diff Patch for nvi 1.34.
+nvi-1.76.diff Patch for nvi 1.76.
+nvi-1.79.diff Patch for nvi 1.79.
diff --git a/usr.bin/global/Makefile b/usr.bin/global/Makefile
new file mode 100644
index 0000000..2eb181e
--- /dev/null
+++ b/usr.bin/global/Makefile
@@ -0,0 +1,3 @@
+SUBDIR= gctags global gtags btreeop htags systags
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/global/Makefile.inc b/usr.bin/global/Makefile.inc
new file mode 100644
index 0000000..c24c4b1
--- /dev/null
+++ b/usr.bin/global/Makefile.inc
@@ -0,0 +1 @@
+BINDIR?= /usr/bin
diff --git a/usr.bin/global/README b/usr.bin/global/README
new file mode 100644
index 0000000..e69fba0
--- /dev/null
+++ b/usr.bin/global/README
@@ -0,0 +1,527 @@
+
+ @@@@@@@-
+ @- @-
+ @- @-
+ @- @- @@@@@- @@@@@@- @@- @-
+ @- F o r a l l h a c h e r s. version 1.9
+ @- @@@@@@-@- @- @- @@@@@- @- @- @-
+ @- @- @- @- @- @- @- @@@@@- @-
+ @- @- @- @- @- @- @-@- @- @-
+ @@@@@@@@- @@@@- @@@@@- @@@@@@-@@@- @@@@- @@@@@@-
+
+ Shigio Yamaguchi 21-Apr-97
+
+ Copyright 1996, 1997 Shigio Yamaguchi All right resereved.
+
+GLOBAL is a browsing system for C and Yacc source code.
+It brings benefits to all hackers. Enjoy!
+
+ Contents
+ --------------------------------
+ 0. Introduction
+
+ 1. Global
+
+ 1.1. Features
+ 1.2. Preparation
+ 1.3. Basic usage
+ 1.4. Applied usage
+
+ 2. Extended vi using global
+
+ 2.1. Features
+ 2.2. Preparation
+ 2.3. Basic usage
+ 2.4. Applied usage
+
+ 3. Hypertext generator
+
+ 3.1. Features
+ 3.2. Preparation
+ 3.3. Usage
+ 3.4. To make hypertext of kernel
+ --------------------------------
+
+0. Introduction
+
+GLOBAL is a browsing system for C and Yacc source files.
+You can locate the specified function in C source files and move there easily.
+It is useful to hack a large project containing many subdirectories,
+many '#ifdef' and many main() functions like MH, X or BSD kernel.
+
+It supports following environments.
+
+ o shell command line(see '1. Global')
+ o vi editor(see '2. Extended vi using global')
+ o web browser(see '3. Hypertext generator')
+
+GLOBAL is consist of global(1), gtags(1), btreeop(1), gctags(1), htags(1)
+and extended vi(1).
+
+ * 'extended' means being entended for GLOBAL.
+
+ * Btreeop and gctags are used internally, so you need not
+ understand about them.
+
+The extended vi is completely upper compatible with original one.
+All the functions for GLOBAL are enabled only in 'gtagsmode'.
+
+------------------------------------------------------------------------------
+
+1. Global
+
+1.1. Features
+
+ o Global can find the locations of a specified function quickly.
+ o Global can locate not only function definitions but also function references.
+ o Global allows duplicate entries.
+ o Global can treat a source tree containing subdirectories and you can
+ get relative path of objects from anywhere within the tree.
+ o Global can understand perl's regular expression.
+ o Global can search in not only a source tree but also library paths.
+ o Global can treat yacc source file.
+
+ I think these features are useful for a large project containing many
+ subdirectories, many '#ifdef' and many main() functions like MH.
+
+1.2. Preparation
+
+ First of all, you must execute gtags(1) at the root of source tree.
+ For example, if you want to browse vi's source code, please do like this.
+
+ % cd /usr/src/usr.bin/vi
+ % gtags
+
+ Gtags traverse subdirectories and makes
+ two database at the root of source tree.
+
+ % ls G*TAGS
+ GRTAGS GTAGS
+
+ GTAGS - database for function definition
+ GRTAGS - database for function reference
+
+ If you cannot find functions that should exist, please try -e option.
+
+ % gtags -e
+
+1.3. Basic usage
+
+ Please think of following source tree.
+
+ ROOT/ <- the root of source tree (GTAGS,GRTAGS)
+ |
+ |- DIR1/
+ | |
+ | |- fileA.c ..... +---------------+
+ | | |main(){ |
+ | | | func1();|
+ | | | func2();|
+ | | |} |
+ | | +---------------+
+ | |
+ | |- fileB.c ..... +---------------+
+ | |func1(){ ... } |
+ | +---------------+
+ |- DIR2/
+ |
+ |- fileC.c ..... +---------------+
+ |#ifdef X |
+ |func2(){ i++; }|
+ |#else |
+ |func2(){ i--; }|
+ |#endif |
+ |func3(){ |
+ | func1();|
+ |} |
+ +---------------+
+
+ You can get the relative path of your object from anywhere within
+ the source tree.
+
+ % cd ROOT
+ % global func1
+ DIR1/fileB.c <- func1() is defined in fileB.c
+ % cd DIR1
+ % global func1
+ fileB.c <- relative path from DIR1
+ % cd ../DIR2
+ % global func1
+ ../DIR1/fileB.c <- relative path from DIR2
+
+ -r option locates function references.
+
+ % global -r func2
+ ../DIR1/fileA.c <- func2() is referred from fileA.c
+
+ You can use perl's regular expression.
+
+ % cd ROOT
+ % global 'func[1-3]'
+ DIR1/fileB.c <- func1, func2 and func3 are matched
+ DIR2/fileC.c
+
+ -x option shows the detail. It's similar to ctags's -x option.
+
+ % global func2
+ DIR2/fileC.c
+ % global -x func2
+ func2 2 DIR2/fileC.c func2(){ i++; }
+ func2 4 DIR2/fileC.c func2(){ i--; }
+
+ -a option produces the absolute path name.
+
+ % global -a func1
+ /home/user/ROOT/DIR1/fileB.c
+
+ You can edit files including specified function directly like this.
+
+ % vi `global func1` <- edit fileB.c
+
+
+1.4. Applied usage
+
+ You can make multiple tag files.
+ For example, you can execute gtags at ROOT/, version1.0/ and version2.0/.
+
+ ROOT/ <- the root of source tree (GTAGS,GRTAGS)
+ |
+ |- version1.0/ <- the root of version1.0 (GTAGS,GRTAGS)
+ | |
+ | |- file.c ..... +---------------+
+ | |func1(){ i++; }|
+ | +---------------+
+ |
+ |- version2.0/ <- the root of version2.0 (GTAGS,GRTAGS)
+ |
+ |- file.c ..... +---------------+
+ |func1(){ i--; }|
+ +---------------+
+
+ When you are walking in version1.0 directory, global locates functions
+ only in version1.0.
+
+ % cd ROOT/version1.0
+ % global -x func1
+ func1 1 file.c func1(){ i++; }
+
+ When you are walking in version2.0, global locates functions only in
+ version2.0.
+
+ % cd ROOT/version2.0
+ % global -x func1
+ func1 1 file.c func1(){ i--; }
+
+ If you are at ROOT/ or you set GTAGSROOT environment variable to ROOT,
+ global locates functions in both version1.0 and version2.0 directories.
+
+ % cd ROOT
+ % global -x func1
+ func1 1 version1.0/file.c func1(){ i++; }
+ func1 1 version2.0/file.c func1(){ i--; }
+
+ =-=-=-=
+
+ There is another usage of GTAGSROOT.
+ If your source files are on a read only device like CDROM, you cannot
+ make database on the root of source tree.
+ In such case, please do the following.
+
+ % mkdir /var/dbpath
+ % cd /cdrom/src <- the root of source tree
+ % gtags /var/dbpath <- make tag file in /var/dbpath
+ % setenv GTAGSROOT `pwd`
+ % setenv GTAGSDBPATH /var/dbpath
+ % global func
+
+ =-=-=-=
+
+ If you want to treat the references to a function that is not defined
+ in the source tree like a library function or system call, you can specify
+ library directories with the GTAGSLIBPATH environment variable.
+ You should execute gtags at each directory of the path.
+ If GTAGS is not found in a directory, global ignores it.
+
+ % pwd
+ /develop/src/mh <- this is the source tree
+ % gtags
+ % ls G*TAGS
+ GRTAGS GTAGS
+ % global mhl
+ uip/mhlsbr.c <- mhl() is found
+ % global strlen <- strlen() is not found
+ % (cd /usr/src/lib; gtags) <- library source
+ % (cd /usr/src/sys; gtags) <- kernel source
+ % setenv GTAGSLIBPATH /usr/src/lib:/usr/src/sys
+ % global strlen
+ ../../../usr/src/lib/libc/string/strlen.c <- strlen() is found in library
+ % global access
+ ../../../usr/src/sys/kern/vfs_syscalls.c <- access() is found in kernel
+
+ Of course, user program doesn't call kernel function directly, but
+ at least it is useful.
+
+ =-=-=-=
+
+ If you forget function name, you can use -c (complete) option.
+
+ % global -c kmem <- maybe k..k.. kmem..
+ kmem_alloc
+ kmem_alloc_pageable
+ kmem_alloc_wait
+ kmem_free
+ kmem_free_wakeup
+ kmem_init
+ kmem_malloc
+ kmem_suballoc <- This is what I need!
+ % global kmem_suballoc
+ ../vm/vm_kern.c
+
+ You can use -c option with tcsh's complete command.
+
+ % set funcs=(`global -c`)
+ % complete global 'n/*/$funcs/'
+ % global kmem_<TAB>
+ kmem_alloc kmem_alloc_wait kmem_free_wakeup kmem_malloc
+ kmem_alloc_pageable kmem_free kmem_init kmem_suballoc
+ % global kmem_s<TAB>
+ % global kmem_suballoc
+ ../vm/vm_kern.c
+
+ * <TAB> means tab key or Ctrl-I.
+
+ =-=-=-=
+
+ If you want to browse many files in order, do the followings.
+
+ % global -xr fork | awk '{printf "view +%s %s\n",$2,$3}' | tee /tmp/list
+ view +650 ../dev/aic7xxx/aic7xxx_asm.c
+ view +250 ibcs2/ibcs2_misc.c
+ view +401 linux/linux_misc.c
+ view +310 ../kern/init_main.c
+ view +318 ../kern/init_main.c
+ view +336 ../kern/init_main.c
+ view +351 ../kern/init_main.c
+ % sh !$ <- from now on, go to next tag with 'ZZ'.
+
+2. Extended vi using global
+
+2.1. Features
+
+ o Tag function of extended vi can locate not only function definitions
+ but also function references.
+ o Extended vi allows duplicate tag entries.
+ o Extended vi can understand perl's regular expression as a tag name
+ for search.
+ o Extended vi is completely upper compatible with original one.
+ Above functions are available only in 'gtags mode'.
+
+2.2. Preparation
+
+ First do the preparation of global. (Please see "1.2. Preparation").
+
+ Second, to use global from vi, you need to get into 'gtagsmode'.
+ There are some ways to do it.
+
+ (a) Start vi with -G option
+
+ % vi -G file.c
+
+ (b) Start vi and execute "set gtagsmode"
+
+ % vi file.c
+ ~
+ ~
+ ~
+ :set gtagsmode
+
+ (c) Previously write the set command to .exrc or .nexrc file and start vi
+
+ $HOME/.exrc
+ +----------------------------
+ |set gtagsmode
+
+ You must start vi under the source tree described in "1.2. Preparation".
+
+2.3. Basic usage
+
+ o To go to func1, you can say
+
+ :tag func1
+
+ It seemes same with original vi, but extended vi use GTAGS
+ instead of tags.
+
+ o To go to referenced point of func1, add prefix 'r'
+
+ :rtag func1
+
+ Extended vi use GRTAGS.
+
+ o If a number of functions located, the action of extended vi differs
+ up to your nvi's version.
+
+ [Extended vi based 1.34 nvi]
+
+ Vi goes into 'GTAGS SELECT MODE' like this.
+
+ +-------------------------------------------------------------
+ |main 347 i386/isa/ultra14f.c main()
+ |main 128 kern/init_main.c main(framep)
+ |main 104 netiso/clnp_debug.c main()
+ |main 164 netiso/xebec/main.c main(argc, argv)
+ |~
+ |~
+ |~
+ |~
+ |~
+ |[GTAGS SELECT MODE] 4 lines
+ +-------------------------------------------------------------
+
+ You can select a tag line by any vi command and press [RETURN],
+ and you can go to the tag's point. In ex mode, type "select"
+ instead of [RETURN]. When you want to go to next or previous tag,
+ you can return to 'GTAGS SELECT MODE' with <control-T> and reselect.
+
+ Suggested .nexrc:
+ set gtagsmode
+ set leftright
+
+ [Extended vi based 1.79 nvi]
+
+ Vi goes to the first tag.
+ Then you can go to next tag by ':tagnext' or back by ':tagprev'.
+
+ Suggested .nexrc:
+ set gtagsmode
+ map ^N :tagnext^M
+ map ^P :tagprev^M
+
+ == WHY TWO STYLE EXIST ? ==
+ 1.34 nvi cannot treat duplicate tag entries, so I made 'GTAGS SELECT MODE'
+ in it. But 1.79 nvi (1.61 and later) can treat them, so I adapted GLOBAL
+ tags to nvi's tag structure.
+
+ o <control-]> command is available.
+
+ In gtagsmode, if you are on the first column of line, it is identical to
+ ":rtag <current token>[RETURN]", otherwise ":tag <current token>[RETURN]".
+
+ o Other tag commands are available too.
+
+ <control-T>
+ ":tagpop"
+ ":tagtop"
+ ":display tags"
+
+ Please read online manual.
+
+2.4. Applied usage
+
+ o In large project which include many main() function like MH,
+ you can start vi like this.
+
+ % vi -G -t main
+
+ You can browse all commands sequentially.
+
+ o When you want to check functions the name of which start with
+ "set" or "get",
+
+ % vi -G -t '^[sg]et'
+
+ Of cause, following command is available too.
+
+ :tag ^[sg]et
+
+ o If your source files are on a read only device like CDROM, please do
+ the followings.
+
+ % mkdir /var/dbpath <- directory for tag file
+ % cd /cdrom/src <- the root of source tree
+ % gtags /var/dbpath <- make tag files in /var/dbpath
+ % setenv GTAGSROOT `pwd`
+ % setenv GTAGSDBPATH /var/dbpath
+ % vi -G -t main
+
+ o If you want to treat the references to the function that is not defined
+ in the source tree like library functions or system calls,
+ do the followings.
+
+ % cd /usr/src/lib
+ % gtags <- probably as a root
+ % cd /usr/src/sys
+ % gtags
+ % setenv GTAGSLIBPATH /usr/src/lib:/usr/src/sys
+
+ If you examine vi's source,
+
+ % cd /usr/src/usr.bin/vi
+ % gtags
+ % vi -G -t main
+
+ You can start from vi and trip the whole unix world as if using
+ hypertext.
+
+
+3. Hypertext generator
+
+3.1. Features
+
+ o Htags makes hypertext from C source files.
+ o Once the hypertext generated, you need nothing other than WWW browser.
+ o You can use all of your browser's functions, for example, search,
+ history, bookmark, save, frame, windows and so on.
+
+3.2. Preparation
+
+ At first, you must prepare much disk space. Hypertext needs so much
+ disk space. For example, the source code of FreeBSD kernel needs the
+ following disk space.
+
+ source code(/usr/src/sys) 14MB
+ tag database(GTAGS,GRTAGS) 9MB(!)
+ hypertext(HTML/*) 45MB(!!!)
+
+ Please do the followings.
+
+ (at your source directory)
+ % gtags <- make tag database
+ % htags <- make hypertext
+
+ Then you will find 'HTML' directory in the current directory.
+
+3.3. Usage
+
+ Please start a web browser like this.
+
+ % lynx HTML/index.html
+
+ You can use any browsers, for example, Lynx, Chimera, Mosaic,
+ Netscape Navigator, Internet Explorer and so on.
+ (But IE3.0 doesn't work well about index.)
+
+ You will understand the usage for the looking.
+ You can move HTML directory to anywhere. It is independent of
+ the source code.
+
+3.4. To make hypertext of kernel
+
+ If you would like to make hypertext of FreeBSD or Linux kernel source,
+ it is convenient to use systags script in this package.
+
+ % cd /usr/src/sys
+ % systags
+ then
+
+ % netscape HTML/index.html
+
+ You can use one level nested index and browse assembler source file too.
+
+Thank you for your reading of my poor english.
+And of course, I'm grateful to Keith Bostic for his excellent nvi(1) and db(3).
+----------------------------------------------------------------------------
+E-Mail: <shigio@wafu.netgate.net>
+WWW: <http://wafu.netgate.net/tama/unix/indexe.html>
+ (You can find the latest version here.)
+----------------------------------------------------------------------------
diff --git a/usr.bin/global/VERSION b/usr.bin/global/VERSION
new file mode 100644
index 0000000..2e0e38c
--- /dev/null
+++ b/usr.bin/global/VERSION
@@ -0,0 +1 @@
+1.9
diff --git a/usr.bin/global/btreeop/Makefile b/usr.bin/global/btreeop/Makefile
new file mode 100644
index 0000000..c9e0409
--- /dev/null
+++ b/usr.bin/global/btreeop/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 1.0 (Berkeley) 4/21/96
+
+PROG= btreeop
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/global/btreeop/btreeop.1 b/usr.bin/global/btreeop/btreeop.1
new file mode 100644
index 0000000..08bbf51
--- /dev/null
+++ b/usr.bin/global/btreeop/btreeop.1
@@ -0,0 +1,168 @@
+.\"
+.\" Copyright (c) 1996, 1997 Shigio Yamaguchi. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Shigio Yamaguchi.
+.\" 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 THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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 April 21, 1997
+.Dt BTREEOP 1
+.Os BSD 4
+.Sh NAME
+.Nm btreeop
+.Nd btree database maintenance tool
+.Sh SYNOPSIS
+.Nm btreeop
+.Op Fl A
+.Op Fl C
+.Op Fl D Ar key
+.Op Fl K Ar key
+.Op Fl b
+.Op Fl c Ar cashesize
+.Op Fl l
+.Op Fl p Ar psize
+.Op Ar dbname
+.Sh DESCRIPTION
+.Nm Btreeop
+execute simple operations for
+.Xr btree 3
+database.
+.Nm Btreeop
+can create database, write record, read record (sequential or index) and
+delete record from it.
+Duplicate entries are allowed.
+.Sh OPTIONS
+A capital letter means a command. If no command specified
+then it assume sequential read operation.
+.Bl -tag -width Ds
+.It Fl A
+append records. If database doesn't exist, btreeop creates it.
+.It Fl C
+create database and write records to it.
+.It Fl D Ar key
+delete records by the key.
+.It Fl K Ar key
+search records by the key.
+.It Fl b
+assume BIG_ENDIAN byte order. default is LITTLE_ENDIAN.
+.It Fl c Ar cashesize
+specify cashesize. It's identical to
+.Nm info.cachesize
+of BTREEINFO. (see btree(3))
+.It Fl l
+assume LITTLE_ENDIAN byte order. (the default)
+.It Fl p Ar psize
+specify page size. It's identical to
+.Nm info.psize
+of BTREEINFO. (see btree(3))
+.It Ar dbname
+database name. default is 'btree'.
+.Sh DATA FORMAT
+To creat (or append) database,
+.Nm btreeop
+read data from stdin.
+The format of the data is the following.
+
+ Key Data\\n
+ Key Data\\n
+ .
+ .
+ .
+
+.El
+
+.Bl -enum -offset indent
+.It
+Key and Data are separated by blank('\\t' or ' ').
+.It
+Key cannot include blank.
+.It
+Data can include blank.
+.It
+Null Data not allowed.
+.It
+Additionally, META record is available. META record has a key that start with
+a blank. You can read this record only by indexed search (with -K option).
+Usage is unlimited by Btreeop.
+.El
+.Sh EXAMPLES
+Create database.
+
+ % btreeop -C
+ key1 data1
+ key2 data2
+ key3 data3
+ ^D
+ %
+
+Append records.
+
+ % btreeop -A
+ __.VERSION 2
+ key2 data2-2
+ ^D
+ %
+
+Sequential read.
+
+ % btreeop
+ key2 data2
+ key3 data3
+ key2 data2-2
+ key1 data1
+ %
+
+Indexed read.
+
+ % btreeop -K key2
+ key2 data2-2
+ key2 data2
+ % btreeop -K ' __.VERSION'
+ __.VERSION 2
+ %
+
+Delete record.
+
+ % btreeop -D ' __.VERSION'
+ % btreeop -K ' __.VERSION'
+ %
+
+.Sh FILES
+.Bl -tag -width tags -compact
+.It Pa btree
+default database name.
+.El
+.Sh DIAGNOSTICS
+.Nm Btreeop
+exits with a value of 1 if an error occurred, 0 otherwise.
+.Sh SEE ALSO
+.Xr btree 3
+.Sh AUTHOR
+Shigio Yamaguchi (shigio@wafu.netgate.net)
+.Sh HISTORY
+The
+.Nm
+command appeared in FreeBSD 2.2.
diff --git a/usr.bin/global/btreeop/btreeop.c b/usr.bin/global/btreeop/btreeop.c
new file mode 100644
index 0000000..a202578
--- /dev/null
+++ b/usr.bin/global/btreeop/btreeop.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 1996, 1997 Shigio Yamaguchi. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Shigio Yamaguchi.
+ * 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 THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * btreeop.c 21-Apr-97
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <db.h>
+#include <fcntl.h>
+
+char *dbdefault = "btree"; /* default database name */
+char *progname = "btreeop"; /* command name */
+char *dbname;
+char buf[BUFSIZ+1];
+
+#ifndef __P
+#if defined(__STDC__)
+#define __P(protos) protos
+#else
+#define __P(protos) ()
+#endif
+#endif
+void die __P((char *));
+void usage __P((void));
+void entab __P((char *));
+void main __P((int, char **));
+int dbwrite __P((DB *));
+int dbkey __P((DB *, char *));
+int dbscan __P((DB *));
+int dbdel __P((DB *, char *));
+DB *db;
+char *key;
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+void
+die(s)
+char *s;
+{
+ fprintf(stderr, "%s: %s\n", progname, s);
+ exit(1);
+}
+
+void
+usage() {
+ fprintf(stderr,
+ "usage: %s [-A][-C][-D key][-K key][-b][-c cachesize][-l][-p psize][dbname]\n",
+ progname);
+ exit(1);
+}
+
+#define TABPOS(i) ((i)%8 == 0)
+/*
+ * entab: convert spaces into tabs
+ *
+ * io) buf string buffer
+ */
+void
+entab(buf)
+char *buf;
+{
+ int blanks = 0;
+ int pos, src, dst;
+ char c;
+
+ pos = src = dst = 0;
+ while ((c = buf[src++]) != 0) {
+ if (c == ' ') {
+ if (!TABPOS(++pos)) {
+ blanks++; /* count blanks */
+ continue;
+ }
+ buf[dst++] = '\t';
+ } else if (c == '\t') {
+ while (!TABPOS(++pos))
+ ;
+ buf[dst++] = '\t';
+ } else {
+ ++pos;
+ while (blanks--)
+ buf[dst++] = ' ';
+ buf[dst++] = c;
+ }
+ blanks = 0;
+ }
+ buf[dst] = 0;
+}
+
+#include <errno.h>
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ char command = 'R';
+ char *key = NULL;
+ DB *db;
+ BTREEINFO info;
+ int c;
+ int flags;
+ extern char *optarg;
+ extern int optind;
+
+ info.flags = R_DUP; /* allow duplicate entries */
+ info.cachesize = 500000;
+ info.maxkeypage = 0;
+ info.minkeypage = 0;
+ info.psize = 0;
+ info.compare = NULL;
+ info.prefix = NULL;
+ info.lorder = LITTLE_ENDIAN;
+
+ while ((c = getopt(argc, argv, "ACD:K:bc:lp:")) != EOF) {
+ switch (c) {
+ case 'K':
+ case 'D':
+ key = optarg;
+ case 'A':
+ case 'C':
+ if (command != 'R')
+ usage();
+ command = c;
+ break;
+ case 'b':
+ info.lorder = BIG_ENDIAN;
+ break;
+ case 'c':
+ info.cachesize = atoi(optarg);
+ break;
+ case 'l':
+ info.lorder = LITTLE_ENDIAN;
+ break;
+ case 'p':
+ info.psize = atoi(optarg);
+ break;
+ default:
+ usage();
+ }
+ }
+
+ dbname = (optind < argc) ? argv[optind] : dbdefault;
+ switch (command) {
+ case 'A':
+ case 'D':
+ flags = O_RDWR|O_CREAT;
+ break;
+ case 'C':
+ flags = O_RDWR|O_CREAT|O_TRUNC;
+ break;
+ case 'K':
+ case 'R':
+ flags = O_RDONLY;
+ break;
+ }
+ db = dbopen(dbname, flags, 0644, DB_BTREE, &info);
+ if (db == NULL) {
+ die("dbopen failed.");
+ }
+ switch (command) {
+ case 'A': /* Append records */
+ case 'C': /* Create database */
+ dbwrite(db);
+ break;
+ case 'D': /* Delete records */
+ dbdel(db, key);
+ break;
+ case 'K': /* Keyed (indexed) read */
+ dbkey(db, key);
+ break;
+ case 'R': /* sequencial Read */
+ dbscan(db);
+ break;
+ }
+ if (db->close(db)) {
+ die("db->close failed.");
+ }
+ exit(0);
+}
+/*
+ * dbwrite: write to database
+ *
+ * i) db
+ * r) 0: normal
+ */
+int
+dbwrite(db)
+DB *db;
+{
+ DBT key, dat;
+ int status;
+#define IDENTLEN 80
+ char keybuf[IDENTLEN+1];
+ char *c;
+
+ /*
+ * Input file format:
+ * +------------------
+ * |Key Data\n
+ * |Key Data\n
+ * .
+ * .
+ * - Key and Data are separated by blank('\t' or ' ').
+ * - Key cannot include blank.
+ * - Data can include blank.
+ * - Null Data not allowed.
+ *
+ * META record:
+ * You can write meta record by making key start with a ' '.
+ * You can read this record only by indexed read ('-K' option).
+ * +------------------
+ * | __.VERSION 2
+ */
+ while (fgets(buf, BUFSIZ, stdin)) {
+ if (buf[strlen(buf)-1] == '\n') /* chop(buf) */
+ buf[strlen(buf)-1] = 0;
+ else
+ while (fgetc(stdin) != '\n')
+ ;
+ c = buf;
+ if (*c == ' ') { /* META record */
+ if (*++c == ' ')
+ die("illegal format.");
+ }
+ for (; *c && !isspace(*c); c++) /* skip key part */
+ ;
+ if (*c == 0)
+ die("data part not found.");
+ if (c - buf > IDENTLEN)
+ die("key too long.");
+ strncpy(keybuf, buf, c - buf); /* make key string */
+ keybuf[c - buf] = 0;
+ for (; *c && isspace(*c); c++) /* skip blanks */
+ ;
+ if (*c == 0)
+ die("data part is null.");
+ entab(buf);
+ key.data = keybuf;
+ key.size = strlen(keybuf)+1;
+ dat.data = buf;
+ dat.size = strlen(buf)+1;
+
+ status = (db->put)(db, &key, &dat, 0);
+ switch (status) {
+ case RET_SUCCESS:
+ break;
+ case RET_ERROR:
+ case RET_SPECIAL:
+ die("db->put: failed.");
+ }
+ }
+ return(0);
+}
+
+/*
+ * dbkey: Keyed search
+ *
+ * i) db
+ * i) skey
+ * r) 0: normal
+ * 1: not found
+ */
+int
+dbkey(db, skey)
+DB *db;
+char *skey;
+{
+ DBT dat, key;
+ int status;
+
+ key.data = skey;
+ key.size = strlen(skey)+1;
+
+ for (status = (*db->seq)(db, &key, &dat, R_CURSOR);
+ status == RET_SUCCESS && !strcmp(key.data, skey);
+ status = (*db->seq)(db, &key, &dat, R_NEXT)) {
+ (void)fprintf(stdout, "%s\n", (char *)dat.data);
+ }
+ if (status == RET_ERROR)
+ die("db->seq failed.");
+ return (0);
+}
+
+/*
+ * dbscan: Scan all records
+ *
+ * i) db
+ * r) 0: normal
+ * 1: not found
+ */
+int
+dbscan(db)
+DB *db;
+{
+ DBT dat, key;
+ int status;
+
+ for (status = (*db->seq)(db, &key, &dat, R_FIRST);
+ status == RET_SUCCESS;
+ status = (*db->seq)(db, &key, &dat, R_NEXT)) {
+ /* skip META record */
+ if (*(char *)key.data == ' ')
+ continue;
+ (void)fprintf(stdout, "%s\n", (char *)dat.data);
+ }
+ if (status == RET_ERROR)
+ die("db->seq failed.");
+ return (0);
+}
+
+/*
+ * dbdel: Delete records
+ *
+ * i) db
+ * i) key key
+ * r) 0: normal
+ * 1: not found
+ */
+int
+dbdel(db, skey)
+DB *db;
+char *skey;
+{
+ DBT key;
+ int status;
+
+ key.data = skey;
+ key.size = strlen(skey)+1;
+
+ status = (*db->del)(db, &key, 0);
+ if (status == RET_ERROR)
+ die("db->del failed.");
+ return (0);
+}
diff --git a/usr.bin/global/gctags/C.c b/usr.bin/global/gctags/C.c
new file mode 100644
index 0000000..87652b5
--- /dev/null
+++ b/usr.bin/global/gctags/C.c
@@ -0,0 +1,840 @@
+/*
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)C.c 8.4 (Berkeley) 4/2/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.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));
+#ifdef GTAGS
+static int cmp __P((const void *, const void *));
+static int isstatement __P((char *));
+static void define_line __P((void));
+#endif
+
+#ifdef YACC
+extern int yaccfile; /* true when *.y file */
+#endif
+/*
+ * 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 */
+#ifdef YACC
+ /*
+ * yacc file format is like the following.
+ *
+ * declarations
+ * %%
+ * rules
+ * %%
+ * programs
+ *
+ */
+#define DECLARATIONS 0
+#define RULES 1
+#define PROGRAMS 2
+ int yaccstatus = (yaccfile) ? DECLARATIONS : PROGRAMS;
+ int inyacc = (yaccfile) ? YES : NO; /* NO while C source */
+#endif
+
+ 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 '{':
+#ifdef YACC
+ if (yaccstatus == RULES && level == 0)
+ inyacc = NO;
+#endif
+ ++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;
+#ifdef GTAGS
+ /*
+ * -e flag force a function to end when a '}' appear
+ * at column 0. If -e flag not specified, all functions
+ * after funcA() would be lost.
+ *
+ * funcA() {
+ * #ifdef A
+ * if (a) {
+ * ...
+ * #else
+ * if (nota) {
+ * ...
+ * #endif
+ * }
+ * }
+ */
+ if (eflag && ftell(inf) == lineftell+1) {
+ level = 0;
+ }
+#endif
+#if YACC
+ if (yaccstatus == RULES && level == 0)
+ inyacc = YES;
+#endif
+ 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.
+#ifdef GTAGS
+ * in the case of rflag == 1, if we have a current token,
+ * parenthesis on level > zero indicates a function reference.
+#endif
+#ifdef YACC
+ * inyacc == NO while C source.
+#endif
+ */
+ case '(':
+#ifdef YACC
+ if (inyacc == NO)
+#endif
+#ifdef GTAGS
+ if (!rflag && !level && token)
+#else
+ if (!level && token)
+#endif
+ {
+ int curline;
+
+ if (sp != tok)
+ *sp = EOS;
+ /*
+ * grab the line immediately, we may
+ * already be wrong, for example,
+ * foo\n
+ * (arg1,
+ */
+ getline();
+ curline = lineno;
+#ifdef GTAGS
+ /* to make sure. */
+ if (!isstatement(tok))
+#endif
+ if (func_entry()) {
+ ++level;
+ pfnote(tok, curline);
+ }
+ break;
+ }
+#ifdef GTAGS
+ else if (rflag && level && token) {
+ if (sp != tok)
+ *sp = EOS;
+ getline();
+ if (!isstatement(tok) && isdefined(tok))
+ pfnote(tok, lineno);
+ break;
+ }
+#endif
+ 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;
+
+#if YACC
+ case '%':
+ if (yaccstatus == DECLARATIONS || yaccstatus == RULES) {
+ if (GETC(==, '%')) {
+ level = 0;
+ if (yaccstatus == DECLARATIONS) {
+ if (!rflag) {
+ getline();
+ pfnote("yyparse", lineno);
+ }
+ yaccstatus = RULES;
+ } else if (yaccstatus == RULES) {
+ yaccstatus = PROGRAMS;
+ }
+ inyacc = (yaccstatus == PROGRAMS) ? NO : YES;
+ } else if (c == '{') {
+ level = 0;
+ inyacc = NO;
+ } else if (c == '}') {
+ level = 0;
+ inyacc = YES;
+ } else {
+ (void)ungetc(c, inf);
+ }
+ break;
+ }
+ /* else fall throuth */
+#endif
+ /*
+ * store characters until one that can't be part of a token
+ * comes along; check the current token against certain
+ * reserved words.
+ */
+ default:
+#ifdef BUGFIX
+ /*
+ * to treat following function.
+ * func (arg) {
+ * ....
+ * }
+ */
+ if (c == ' ' || c == '\t') {
+ int save = c;
+ while (GETC(!=, EOF) && (c == ' ' || c == '\t'))
+ ;
+ if (c == EOF)
+ return;
+ (void)ungetc(c, inf);
+ c = save;
+ }
+#endif
+ storec: if (!intoken(c)) {
+ if (sp == tok)
+ break;
+ *sp = EOS;
+#ifdef GTAGS
+ if (!memcmp(tok, "extern",7)) {
+ while (GETC(!=, EOF) && c != ';') {
+ if (c == '\n')
+ SETLINE;
+ }
+ if (c == EOF)
+ return;
+ break;
+ }
+#endif
+ 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;
+ /* end of default */
+ } /* end of switch */
+ /*
+ * 'break' statement in switch block come here.
+ */
+ sp = tok;
+ token = NO;
+ } /* end of while */
+}
+
+/*
+ * 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 */
+
+#ifdef BUGFIX
+ /*
+ * to treat following macro.
+ * # macro(arg) ....
+ */
+ while (GETC(!=, EOF) && (c == ' ' || c == '\t'))
+ ;
+ (void)ungetc(c, inf);
+#endif
+ 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;
+#ifdef GTAGS
+ if (rflag) {
+ /*
+ * #define XXX\n
+ */
+ if (c == '\n' || (c == '\r' && GETC(==, '\n'))) {
+ SETLINE;
+ return;
+ }
+ /*
+ * v
+ * #define XXX(X) XXXXXX
+ */
+ if (c == '(')
+ (void)skip_key(')');
+ /*
+ * v
+ * #define XXX(X) XXXXXX
+ */
+ while (GETC(!=, EOF)) {
+ if (c != ' ' && c != '\t') {
+ (void)ungetc(c, inf);
+ break;
+ }
+ }
+ /*
+ * v
+ * #define XXX(X) XXXXXX
+ */
+ define_line();
+ return;
+ }
+#endif
+ if (dflag || c == '(') { /* only want macros */
+ getline();
+ pfnote(tok, curline);
+ }
+skip: if (c == '\n') { /* get rid of rest of define */
+ SETLINE
+#ifdef MODIFY
+ if (*(sp - 1) == '\r') {
+ if (*(sp - 2) != '\\')
+ return;
+ } else
+#endif
+ if (*(sp - 1) != '\\')
+ return;
+ }
+ (void)skip_key('\n');
+}
+
+#ifdef GTAGS
+ /* sorted by alphabet */
+static struct words {
+ char *name;
+} words[] = {
+ {"__P"},
+ {"auto"},
+ {"break"},
+ {"case"},
+ {"char"},
+ {"continue"},
+ {"default"},
+ {"do"},
+ {"double"},
+ {"else"},
+ {"extern"},
+ {"float"},
+ {"for"},
+ {"goto"},
+ {"if"},
+ {"int"},
+ {"long"},
+ {"register"},
+ {"return"},
+ {"short"},
+ {"sizeof"},
+ {"static"},
+ {"struct"},
+ {"switch"},
+ {"typedef"},
+ {"union"},
+ {"unsigned"},
+ {"void"},
+ {"while"},
+};
+
+static int
+cmp(s1, s2)
+ const void *s1, *s2;
+{
+ return strcmp(((struct words *)s1)->name, ((struct words *)s2)->name);
+}
+
+static int
+isstatement(token)
+ char *token;
+{
+ struct words tmp;
+
+ tmp.name = token;
+ if (bsearch(&tmp, words, sizeof(words)/sizeof(struct words), sizeof(struct words), cmp))
+ return YES;
+ return NO;
+}
+
+static void
+define_line()
+{
+ int c; /* character read */
+ int level; /* brace level */
+ int token; /* if reading a token */
+ char *sp; /* buffer pointer */
+ char tok[MAXTOKEN]; /* storage buffer */
+
+ sp = tok; token = NO; level = 0;
+ while (GETC(!=, EOF)) {
+ switch (c) {
+ case '{':
+ ++level;
+ goto endtok;
+ case '}':
+ if (--level < 0)
+ level = 0;
+ goto endtok;
+
+ case '\\':
+ if (GETC(==, '\n') || (c == '\r' && GETC(==, '\n'))) {
+ SETLINE;
+ }
+ continue;
+
+ case '\n':
+ SETLINE;
+ return;
+ endtok: if (sp > tok) {
+ *sp = EOS;
+ token = YES;
+ sp = tok;
+ }
+ else
+ token = NO;
+ continue;
+
+ case '"':
+ case '\'':
+ (void)skip_string(c);
+ break;
+
+ case '/':
+ if (GETC(==, '*')) {
+ skip_comment();
+ continue;
+ }
+ (void)ungetc(c, inf);
+ c = '/';
+ goto storec;
+
+ case '(':
+ if (token) {
+ if (sp != tok)
+ *sp = EOS;
+ getline();
+ if (!isstatement(tok) && isdefined(tok))
+ pfnote(tok, lineno);
+ break;
+ }
+ goto storec;
+
+ case ';':
+ goto storec;
+
+ default:
+storec: if (!intoken(c)) {
+ if (sp == tok)
+ break;
+ *sp = EOS;
+ sp = tok;
+ }
+ else if (sp != tok || begtoken(c)) {
+ *sp++ = c;
+ token = YES;
+ }
+ continue;
+ }
+
+ sp = tok;
+ token = NO;
+ }
+}
+#endif
+/*
+ * 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.
+ */
+static 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;
+#ifdef MODIFY
+ case '\r':
+ break;
+#endif
+ 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;
+#ifdef MODIFY
+ case '\r':
+ break;
+#endif
+ case '\n':
+ SETLINE;
+ /*FALLTHROUGH*/
+ default:
+ norm:
+ if (c == key && !skip)
+ return (retval);
+ skip = NO;
+ }
+ return (retval);
+}
diff --git a/usr.bin/global/gctags/Makefile b/usr.bin/global/gctags/Makefile
new file mode 100644
index 0000000..bc0cbbf
--- /dev/null
+++ b/usr.bin/global/gctags/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= gctags
+CFLAGS+=-I${.CURDIR} -DGTAGS -DBUGFIX -DMODIFY -DYACC
+SRCS= C.c ctags.c fortran.c lisp.c print.c tree.c yacc.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/global/gctags/ctags.c b/usr.bin/global/gctags/ctags.c
new file mode 100644
index 0000000..f73a6bd
--- /dev/null
+++ b/usr.bin/global/gctags/ctags.c
@@ -0,0 +1,404 @@
+/*
+ * 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 */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)ctags.c 8.3 (Berkeley) 4/2/94";
+#endif /* LIBC_SCCS and not lint */
+
+#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 */
+#ifdef GTAGS
+int eflag; /* -e: '{' at 0 column force function end */
+#endif
+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 */
+#ifdef GTAGS
+int Dflag; /* -D: allow duplicate entrys */
+int rflag; /* -r: function reference */
+#endif
+#ifdef YACC
+int yaccfile; /* yacc file */
+#endif
+
+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 __P((int, 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 */
+ extern char *optarg;
+ extern int optind;
+
+#ifndef lint
+ copyright[0] = copyright[0]; /* to satisfy compiler */
+#endif
+ aflag = uflag = NO;
+#ifdef GTAGS
+ while ((ch = getopt(argc, argv, "BDFadef:rtuwvxy")) != EOF)
+#else
+ while ((ch = getopt(argc, argv, "BFadf:tuwvx")) != EOF)
+#endif
+ switch(ch) {
+ case 'B':
+ searchar = '?';
+ break;
+#ifdef GTAGS
+ case 'D':
+ Dflag++;
+ break;
+#endif
+ case 'F':
+ searchar = '/';
+ break;
+ case 'a':
+ aflag++;
+ break;
+ case 'd':
+ dflag++;
+ break;
+#ifdef GTAGS
+ case 'e':
+ eflag++;
+ break;
+#endif
+ case 'f':
+ outfile = optarg;
+ break;
+#ifdef GTAGS
+ case 'r':
+ rflag++;
+ break;
+#endif
+ 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,
+#ifdef GTAGS
+ "usage: ctags [-BDFadrtuwvx] [-f tagsfile] file ...\n");
+#else
+ "usage: ctags [-BFadtuwvx] [-f tagsfile] file ...\n");
+#endif
+ exit(1);
+ }
+#ifdef GTAGS
+ if (rflag)
+ gtagopen();
+#endif
+ init();
+
+ for (exit_val = step = 0; step < argc; ++step)
+ if (!(inf = fopen(argv[step], "r"))) {
+ fprintf(stderr, "gctags: %s cannot open\n", 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"))) {
+ fprintf(stderr, "gctags: %s cannot open\n", outfile);
+ exit(exit_val);
+ }
+ put_entries(head);
+ (void)fclose(outf);
+ if (uflag) {
+ (void)sprintf(cmd, "sort -o %s %s",
+ outfile, outfile);
+ system(cmd);
+ }
+ }
+#ifdef GTAGS
+ if (rflag)
+ gtagclose();
+#endif
+ 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 = (unsigned char *)CWHITE; *sp; sp++) /* white space chars */
+ _wht[*sp] = YES;
+#define CTOKEN " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?"
+ for (sp = (unsigned char *)CTOKEN; *sp; sp++) /* token ending chars */
+ _etk[*sp] = YES;
+#define CINTOK "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789"
+ for (sp = (unsigned char *)CINTOK; *sp; sp++) /* valid in-token chars */
+ _itk[*sp] = YES;
+#define CBEGIN "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
+ for (sp = (unsigned char *)CBEGIN; *sp; sp++) /* token starting chars */
+ _btk[*sp] = YES;
+#define CNOTGD ",;"
+ for (sp = (unsigned char *)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, '.')) != NULL) {
+ if (cp[1] == 'l' && !cp[2]) {
+ int c;
+
+#ifdef GTAGS
+ if (rflag)
+ fprintf(stderr, "-r option is ignored in lisp file (Warning only)\n");
+#endif
+ 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]) {
+#ifdef YACC
+ /*
+ * we search all part of a yacc file for C references.
+ * but ignore yacc rule tags.
+ */
+ yaccfile = YES;
+ c_entries();
+ return;
+#endif
+ /*
+ * 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]) {
+#ifdef GTAGS
+ if (rflag)
+ fprintf(stderr, "-r option is ignored in fortran file (Warning only)\n");
+#endif
+ if (PF_funcs())
+ return;
+ rewind(inf);
+ }
+ }
+#ifdef YACC
+ yaccfile = NO;
+#endif
+/* C */ c_entries();
+}
+
+#ifdef GTAGS
+#include <db.h>
+DB *db;
+
+void
+gtagopen()
+{
+ BTREEINFO info;
+ char *env;
+ char dbname[200];
+
+ strcpy(dbname, ".");
+ if ((env = getenv("GTAGDBPATH"))) {
+ strcpy(dbname, env);
+ }
+ strcat(dbname, "/GTAGS");
+
+ info.flags = 0;
+ info.cachesize = 500000;
+ info.maxkeypage = 0;
+ info.minkeypage = 0;
+ info.psize = 0;
+ info.compare = 0;
+ info.prefix = 0;
+ info.lorder = 0;
+
+#define O_RDONLY 0x0000 /* open for reading only */
+ db = dbopen(dbname, O_RDONLY, 0, DB_BTREE, &info);
+ if (db == 0) {
+ fprintf(stderr, "GTAGS file needed.\n");
+ exit(1);
+ }
+}
+int
+isdefined(skey)
+char *skey;
+{
+ DBT dat, key;
+ int status;
+
+ key.data = skey;
+ key.size = strlen(skey)+1;
+
+ status = (*db->get)(db, &key, &dat, 0);
+ switch (status) {
+ case RET_SUCCESS:
+ return(1); /* exist */
+ case RET_ERROR:
+ fprintf(stderr, "db->get failed.\n");
+ exit(1);
+ case RET_SPECIAL: /* not exist */
+ break;
+ }
+ return 0;
+}
+void
+gtagclose()
+{
+ if (db->close(db)) {
+ fprintf(stderr, "GTAGS cannot close.(dbclose)\n");
+ exit(1);
+ }
+}
+#endif
diff --git a/usr.bin/global/gctags/ctags.h b/usr.bin/global/gctags/ctags.h
new file mode 100644
index 0000000..7fa84b8
--- /dev/null
+++ b/usr.bin/global/gctags/ctags.h
@@ -0,0 +1,110 @@
+/*
+ * 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 */
+
+#ifndef LINE_MAX
+#define LINE_MAX 2048
+#endif
+
+#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 */
+#ifdef GTAGS
+extern int eflag; /* -e: '{' at 0 column force function end */
+extern int Dflag; /* -D: allow duplicate entrys */
+extern int rflag; /* -r: function reference */
+#endif /* GTAGS */
+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 */
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(protos) protos
+#else
+#define __P(protos) ()
+#endif
+#endif
+
+int cicmp __P((char *));
+void getline __P((void));
+void pfnote __P((char *, int));
+int skip_key __P((int));
+void put_entries __P((NODE *));
+void toss_yysec __P((void));
+void l_entries __P((void));
+void y_entries __P((void));
+int PF_funcs __P((void));
+void c_entries __P((void));
+void skip_comment __P((void));
+void gtagopen __P((void));
+int isdefined __P((char *));
+void gtagclose __P((void));
diff --git a/usr.bin/global/gctags/fortran.c b/usr.bin/global/gctags/fortran.c
new file mode 100644
index 0000000..2a33aff
--- /dev/null
+++ b/usr.bin/global/gctags/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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)fortran.c 8.3 (Berkeley) 4/2/94";
+#endif /* LIBC_SCCS and 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/global/gctags/gctags.1 b/usr.bin/global/gctags/gctags.1
new file mode 100644
index 0000000..224ea76
--- /dev/null
+++ b/usr.bin/global/gctags/gctags.1
@@ -0,0 +1,227 @@
+.\" 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.
+.\"
+.\" @(#)gctags.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd April 21, 1997
+.Dt GCTAGS 1
+.Os BSD 4
+.Sh NAME
+.Nm gctags
+.Nd create a tags file (special command for GLOBAL)
+.Sh SYNOPSIS
+.Nm gctags
+.Op Fl BDFadertuwvx
+.Op Fl f Ar tagsfile
+.Ar name ...
+.Sh DESCRIPTION
+.Nm Gctags
+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 gctags ,
+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 D
+allow duplicate object names.
+.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 e
+force a function to end when reach a '}' at the first column. (C source only)
+.It Fl f
+place the tag descriptions in a file called
+.Ar tagsfile .
+The default behavior is to place them in a file called
+.Ar tags .
+.It Fl r
+locate function references instead of function definitions. GTAGS file is
+needed at the current directory. (C source only)
+.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
+gctags \-v files \&| sort \-f > index
+vgrind \-x index
+.Ed
+.It Fl w
+suppress warning diagnostics.
+.It Fl x
+.Nm gctags
+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 gctags
+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
+.It Pa GTAGS
+tags file for GLOBAL
+.El
+.Sh DIAGNOSTICS
+.Nm Gctags
+exits with a value of 1 if an error occurred, 0 otherwise.
+Duplicate objects are not considered errors.
+.Sh SEE ALSO
+.Xr btreeop 1 ,
+.Xr ex 1 ,
+.Xr global 1 ,
+.Xr gtags 1 ,
+.Xr htags 1 ,
+.Xr vi 1 .
+.Sh BUGS
+.Pp
+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 Gctags
+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 Gctags
+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 FreeBSD 2.2.
diff --git a/usr.bin/global/gctags/lisp.c b/usr.bin/global/gctags/lisp.c
new file mode 100644
index 0000000..ebf5184
--- /dev/null
+++ b/usr.bin/global/gctags/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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)lisp.c 8.3 (Berkeley) 4/2/94";
+#endif /* LIBC_SCCS and 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/global/gctags/print.c b/usr.bin/global/gctags/print.c
new file mode 100644
index 0000000..692d36b
--- /dev/null
+++ b/usr.bin/global/gctags/print.c
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)print.c 8.3 (Berkeley) 4/2/94";
+#endif /* LIBC_SCCS and 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, SEEK_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, SEEK_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)
+#ifdef MODIFY
+ /* separate 'entry' and 'lno' */
+ if (strlen(node->entry) >= 16 && node->lno >= 1000)
+ printf("%-16s %4d %-16s %s\n",
+ node->entry, node->lno, node->file, node->pat);
+ else /* for compatibility */
+#endif
+ 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/global/gctags/test/ctags.test b/usr.bin/global/gctags/test/ctags.test
new file mode 100644
index 0000000..1f334ac
--- /dev/null
+++ b/usr.bin/global/gctags/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/global/gctags/tree.c b/usr.bin/global/gctags/tree.c
new file mode 100644
index 0000000..831691a
--- /dev/null
+++ b/usr.bin/global/gctags/tree.c
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)tree.c 8.3 (Berkeley) 4/2/94";
+#endif /* LIBC_SCCS and not lint */
+
+#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)))) {
+ fprintf(stderr, "too many entries to sort");
+ put_entries(head);
+ free_tree(head);
+ /*NOSTRICT*/
+ if (!(head = np = (NODE *)malloc(sizeof(NODE)))) {
+ fprintf(stderr, "gctags: out of space\n");
+ exit(1);
+ }
+ }
+ 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))) {
+ fprintf(stderr, "gctags: out of space\n");
+ exit(1);
+ }
+ np->file = curfile;
+ np->lno = ln;
+ np->left = np->right = 0;
+ if (!(np->pat = strdup(lbuf))) {
+ fprintf(stderr, "gctags: out of space\n");
+ exit(1);
+ }
+ 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);
+#ifdef GTAGS
+ if (!Dflag && !dif) /* -D option allows duplicate entries. */
+#else
+ if (!dif)
+#endif
+ {
+ 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/global/gctags/yacc.c b/usr.bin/global/gctags/yacc.c
new file mode 100644
index 0000000..9dcdd5a
--- /dev/null
+++ b/usr.bin/global/gctags/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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)yacc.c 8.3 (Berkeley) 4/2/94";
+#endif /* LIBC_SCCS and 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/global/global/Makefile b/usr.bin/global/global/Makefile
new file mode 100644
index 0000000..256ea71
--- /dev/null
+++ b/usr.bin/global/global/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 1.0 (Berkeley) 4/21/96
+
+MAN1= global.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/global.pl ${DESTDIR}/usr/bin/global
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/global/global/global.1 b/usr.bin/global/global/global.1
new file mode 100644
index 0000000..613a915
--- /dev/null
+++ b/usr.bin/global/global/global.1
@@ -0,0 +1,136 @@
+.\"
+.\" Copyright (c) 1996, 1997 Shigio Yamaguchi. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Shigio Yamaguchi.
+.\" 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 THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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 April 21, 1997
+.Dt GLOBAL 1
+.Os BSD 4
+.Sh NAME
+.Nm global
+.Nd print the locations of specified function.
+.Sh SYNOPSIS
+.Nm global
+.Op Fl acrx
+.Ar name
+.Sh DESCRIPTION
+.Nm Global
+find the locations of specified function in C and Yacc source files.
+.Nm Global
+can treat a source tree, that is, a directory that has subdirectories and
+source files.
+You can get the relative path of objects from anywhere within the tree.
+
+.Nm Global
+can locate not only function definitions but also function references and
+allow duplicate entries too.
+.Pp
+In advance of using this command, you must execute
+.Xr gtags 1
+at the root directory of the source tree.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl a
+print absolute path name. By default, print relative path name.
+.It Fl c
+print candidate function names which start with specified string.
+If string is not specified, print all function names.
+.It Fl r
+print the locations of function references. By default, print function
+definitions.
+.It Fl x
+In addition to the default output, produce the line number and
+the line contents.
+.It Ar name
+function name. It can include perl's regular expression.
+.Sh FILES
+.Bl -tag -width tags -compact
+.It Pa GTAGS
+tags file for function definitions.
+.It Pa GRTAGS
+tags file for function references.
+.El
+.Sh ENVIRONMENT
+The following environment variables affect the execution of global.
+.Pp
+.Bl -tag -width indent
+.It Ev GTAGSROOT
+The directory which is the root of source tree.
+.It Ev GTAGSDBPATH
+The directory on which gtags database exist. This value is ignored
+when GTAGSROOT is not defined.
+.It Ev GTAGSLIBPATH
+If this variable is set, its value is used as the path to search for library
+functions. If specified function is not found in a source tree,
+global search in these path too.
+.Sh EXAMPLES
+
+ % ls -F
+ Makefile src/ lib/
+ % gtags
+ % global main
+ src/main.c
+ % global -x main
+ main 10 src/main.c main (argc, argv) {
+ % global -x '^[sg]et'
+ set_num 20 lib/util.c set_num(values)
+ get_num 30 lib/util.c get_num() {
+ % global -rx '^[sg]et'
+ set_num 113 src/op.c set_num(32);
+ set_num 225 src/opop.c if (set_num(0) > 0) {
+ get_num 90 src/op.c while (get_num() > 0) {
+ % cd lib
+ % global -rx '^[sg]et'
+ set_num 113 ../src/op.c set_num(32);
+ set_num 225 ../src/opop.c if (set_num(0) > 0) {
+ get_num 90 ../src/op.c while (get_num() > 0) {
+ % global strlen
+ % (cd /usr/src/sys; gtags)
+ % setenv GTAGSLIBPATH /usr/src/sys
+ % global strlen
+ ../../../usr/src/sys/libkern/strlen.c
+ % (cd /usr/src/lib; gtags)
+ % setenv GTAGSLIBPATH /usr/src/lib:/usr/src/sys
+ % global strlen
+ ../../../usr/src/lib/libc/string/strlen.c
+
+.Sh DIAGNOSTICS
+.Nm Global
+exits with a non 0 value if an error occurred, 0 otherwise.
+.Sh SEE ALSO
+.Xr btreeop 1 ,
+.Xr gctags 1 ,
+.Xr gtags 1 ,
+.Xr htags 1 .
+.Sh AUTHORS
+Shigio Yamaguchi (shigio@wafu.netgate.net)
+.Sh HISTORY
+The
+.Nm
+command appeared in FreeBSD 2.2.
diff --git a/usr.bin/global/global/global.pl b/usr.bin/global/global/global.pl
new file mode 100644
index 0000000..ff00340
--- /dev/null
+++ b/usr.bin/global/global/global.pl
@@ -0,0 +1,232 @@
+#!/usr/bin/perl
+#
+# Copyright (c) 1996, 1997 Shigio Yamaguchi. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Shigio Yamaguchi.
+# 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 THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# global.pl 21-Apr-97
+#
+sub getcwd {
+ local($dir);
+ chop($dir = `/bin/pwd`);
+ $dir;
+}
+$com = $0;
+$com =~ s/.*\///;
+$usage = "usage:\t$com [-a][-r][-x] pattern\n\t$com -c [name]\n";
+#
+# options check
+#
+while ($ARGV[0] =~ /^-/) {
+ $opt = shift;
+ if ($opt =~ /a/) { $aflag = 1; }
+ if ($opt =~ /c/) { $cflag = 1; }
+ if ($opt =~ /r/) { $rflag = 1; }
+ if ($opt =~ /x/) { $xflag = 1; }
+}
+if (@ARGV == 0) {
+ die($usage) if (! $cflag);
+}
+$ARGV[0] =~ s/^[ \t]+//; # remove leading blanks
+if ($ARGV[0] =~ /[][.*\^\$+?|(){}\\]/) { # include regular expression ?
+ $regex = 1;
+}
+if ($cflag) {
+ die($usage) if ($aflag || $rflag || $xflag);
+ die("$com: regular expression not allowed with -c option.\n") if ($regex);
+}
+$current = &getcwd;
+#
+# get $dbpath and $root
+#
+if (defined($ENV{'GTAGSROOT'})) {
+ $root = $ENV{'GTAGSROOT'};
+ if (defined($ENV{'GTAGSDBPATH'})) {
+ $dbpath = $ENV{'GTAGSDBPATH'};
+ } else {
+ $dbpath = $root;
+ }
+ unless ($current =~ /$root/) {
+ die("$com: illegal GTAGSROOT.\n");
+ }
+ chdir($dbpath) || die("$com: directory $dbpath not found.\n");
+ $dbpath = &getcwd;
+ chdir($current);
+ chdir($root) || die("$com: directory $root not found.\n");
+ $root = &getcwd;
+}
+chdir($current) || die("$com: cannot return current directory.\n");
+$gtagsname = ($rflag) ? 'GRTAGS' : 'GTAGS';
+#
+# make a sed command to make paths into relative
+#
+if (! defined($root)) {
+ chdir($current);
+ while (! -r $gtagsname && ! -r "obj/$gtagsname") {
+ if (&getcwd =~ m!^/$!) { die "$com: $gtagsname not found.\n"; }
+ chdir('..');
+ }
+ $dbpath = $root = &getcwd;
+ $dbpath = "$dbpath/obj" if (! -r $gtagsname);
+}
+$cur = $current;
+$cur =~ s!$root!!;
+$cur =~ s!^/!!;
+@step = split('/', $cur);
+$downpath = '\\.\\./' x @step;
+push(@com, "-e 's!\\./!$downpath!'");
+foreach $step (@step) {
+ push(@com, "-e 's!\\.\\./$step/!!'");
+}
+#
+# recognize format version of GTAGS. 'format version record' is saved as a
+# META record in GTAGS and GRTAGS. if 'format version record' is not found,
+# it's assumed version 1.
+ $support_version = 1; # accept this format version
+#
+open(GTAGS, "btreeop -K ' __.VERSION' $dbpath/$gtagsname |") || die("$com: GTAGS not found.\n");
+$rec = <GTAGS>;
+close(GTAGS);
+if ($rec =~ /^ __\.VERSION[ \t]+([0-9]+)$/) {
+ $format_version = $1;
+} else {
+ $format_version = 1;
+}
+if ($format_version > $support_version) {
+ die("$com: GTAGS seems new format. Please install the latest GLOBAL.\n");
+}
+#
+# complete function name
+#
+if ($cflag) {
+ open(PIPEIN, "btreeop $dbpath/GTAGS | awk '{print \$1}' | sort | uniq |") || die("$com: btreeop cannot exec.\n");
+ while (<PIPEIN>) {
+ print if (@ARGV == 0 || $_ =~ /^$ARGV[0]/o);
+ }
+ close(PIPEIN);
+ exit(0);
+}
+#
+# search in current source tree.
+#
+$cnt = &search($ARGV[0], $dbpath, $gtagsname, @com);
+#
+# search in library path.
+#
+if ($cnt == 0 && ! $regex && ! $rflag && defined($ENV{'GTAGSLIBPATH'})) {
+ foreach $lib (split(':', $ENV{'GTAGSLIBPATH'})) {
+ next unless (-f "$lib/GTAGS");
+ next if ($dbpath eq $lib);
+ chdir($lib) || die("$com: cannot chdir to $lib.\n");
+ $dbpath = &getcwd;
+ $common = &common($dbpath, $current);
+ $up = $dbpath;
+ $up =~ s/$common//;
+ $down = $current;
+ $down =~ s/$common//;
+ $down =~ s![^/]+!..!g;
+ next if ($down eq '' || $up eq '');
+ $cnt = &search($ARGV[0], $dbpath, 'GTAGS', ("-e 's!\\./!$down/$up/!'"));
+ last if ($cnt > 0);
+ }
+}
+exit(0);
+#
+# common: extract a common part of two paths.
+#
+# i) $p1, $p2 paths
+# r) common part
+#
+sub common {
+ local($p1, $p2) = @_;
+ local(@p1, @p2, @common, $common);
+
+ @p1 = split('/', $p1);
+ @p2 = split('/', $p2);
+ while (@p1 && @p2 && $p1[0] eq $p2[0]) {
+ push(@common, shift @p1);
+ shift @p2;
+ }
+ $common = join('/', @common);
+ $common .= '/';
+ $common;
+}
+#
+# search: search specified function
+#
+# i) $pattern search pattern
+# i) $dbpath where GTAGS exist
+# i) $gtagsname gtags name (GTAGS or GRTAGS)
+# i) @com sed's command
+# gi) $xflag -x option
+# gi) $rflag -r option
+# gi) $regex regular expression
+# r) count of output lines
+#
+sub search {
+ local($pattern, $dbpath, $gtagsname, @com) = @_;
+ local($cnt);
+ #
+ # make input filter
+ #
+ if ($regex) { # regular expression
+ $infilter = "btreeop $dbpath/$gtagsname |";
+ } else {
+ $infilter = "btreeop -K '$pattern' $dbpath/$gtagsname |";
+ }
+ #
+ # make output filter
+ # gtags fields is same to ctags -x format.
+ # 0:tag, 1:lineno, 2:filename, 3: pattern.
+ #
+ if ($xflag) {
+ $outfilter = "| sort +0b -1 +2b -3 +1n -2";
+ } else {
+ $outfilter = "| awk '{print \$3}' | sort | uniq";
+ }
+ #
+ # if absolute path needed
+ #
+ if ($aflag) {
+ @com = ("-e 's!\\.!$dbpath!'");
+ }
+ $outfilter .= "| sed @com";
+ open(PIPEIN, $infilter) || die("$com: database not found.\n");
+ open(PIPEOUT, $outfilter) || die("$com: pipe cannot open.\n");
+ $cnt = 0;
+ while (<PIPEIN>) {
+ ($tag) = split;
+ if (! $regex || $tag =~ /$pattern/o) {
+ $cnt++;
+ print PIPEOUT $_;
+ }
+ }
+ close(PIPEIN);
+ close(PIPEOUT);
+ $cnt;
+}
diff --git a/usr.bin/global/gtags/Makefile b/usr.bin/global/gtags/Makefile
new file mode 100644
index 0000000..cabb890
--- /dev/null
+++ b/usr.bin/global/gtags/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 1.0 (Berkeley) 4/21/96
+
+MAN1= gtags.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/gtags.sh ${DESTDIR}/usr/bin/gtags
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/global/gtags/gtags.1 b/usr.bin/global/gtags/gtags.1
new file mode 100644
index 0000000..c7fddf0
--- /dev/null
+++ b/usr.bin/global/gtags/gtags.1
@@ -0,0 +1,86 @@
+.\"
+.\" Copyright (c) 1996, 1997 Shigio Yamaguchi. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Shigio Yamaguchi.
+.\" 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 THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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 April 21, 1997
+.Dt GTAGS 1
+.Os BSD 4
+.Sh NAME
+.Nm gtags
+.Nd create GTAGS, GRTAGS file
+.Sh SYNOPSIS
+.Nm gtags
+.Op Fl e
+.Op Fl s
+.Op Ar dbpath
+.Sh DESCRIPTION
+.Nm Gtags
+makes GTAGS, GRTAGS files for global(1).
+.Nm Gtags
+trace subdirectories, read source files,
+locate the functions and save the information into tag files.
+C, yacc and assembler source files are supported.
+You should execute this command at the root of the source tree.
+.Pp
+If your source directory is on a read only device like CDROM, specify
+.Ar dbpath
+of the directory on which make tags files.
+.Pp
+.Bl -tag -width Ds
+.It Fl e
+force a function to end when reach a '}' at the first column in C source file.
+.It Fl s
+treat assembler source file (*.s, *.S).
+.Sh FILES
+.Bl -tag -width tags -compact
+.It Pa GTAGS
+tags file for function definitions.
+.It Pa GRTAGS
+tags file for function references.
+.El
+.Sh DIAGNOSTICS
+.Nm Gtags
+exits with a value of 1 if an error occurred, 0 otherwise.
+.Sh SEE ALSO
+.Xr btreeop 1 ,
+.Xr gctags 1 ,
+.Xr global 1 ,
+.Xr htags 1 .
+.Sh BUG
+GTAGS, GRTAGS are very large. In advance, check the space of your disk.
+
+Assembler support is far from completeness. It extracts only ENTRY()
+and ALTENTRY() from source file. Probably valid only for FreeBSD and Linux
+kernel source.
+.Sh AUTHORS
+Shigio Yamaguchi (shigio@wafu.netgate.net)
+.Sh HISTORY
+The
+.Nm
+command appeared in FreeBSD 2.2.
diff --git a/usr.bin/global/gtags/gtags.sh b/usr.bin/global/gtags/gtags.sh
new file mode 100644
index 0000000..79f19bc
--- /dev/null
+++ b/usr.bin/global/gtags/gtags.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+#
+# Copyright (c) 1996, 1997 Shigio Yamaguchi. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Shigio Yamaguchi.
+# 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 THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# gtags.sh 21-Apr-97
+#
+com=`echo $0 | sed 's/.*\///'` # command name
+usage="usage: $com [-e][-s][dbpath]"
+#
+# ctags flag
+#
+eflag=
+sflag=
+while :; do
+ case $1 in
+ -*)
+ if echo $1 | grep '[^-es]' >/dev/null; then
+ echo $usage >/dev/tty; exit 1
+ fi
+ case $1 in
+ -*e*) eflag=e;;
+ esac
+ case $1 in
+ -*s*) sflag=1;;
+ esac
+ shift;;
+ *)
+ break;;
+ esac
+done
+export eflag sflag
+case $1 in
+"") dbpath=".";;
+*) dbpath=$1;;
+esac
+if [ -f $dbpath/GTAGS -a -f $dbpath/GRTAGS ]; then
+ if [ ! -w $dbpath/GTAGS ]; then
+ echo "$com: cannot write to GTAGS."
+ exit 1
+ elif [ ! -w $dbpath/GRTAGS ]; then
+ echo "$com: cannot write to GRTAGS."
+ exit 1
+ fi
+elif [ ! -w $dbpath ]; then
+ echo "$com: cannot write to the directory $dbpath."
+ exit 1
+fi
+#
+# make global database
+#
+for db in GTAGS GRTAGS; do
+ # currently only *.c *.h *.y are supported.
+ # *.s *.S is valid only when -s option specified.
+ find . -type f -name "*.[chysS]" -print | while read f; do
+ case $f in
+ *y.tab.c|*y.tab.h)
+ continue;;
+ *.s|*.S)
+ [ ${sflag}x = x -o $db = GRTAGS ] && continue
+ perl -ne '($nouse, $tag) = /^(ENTRY|ALTENTRY)\((\w+)\)/;
+ if ($tag) {printf("%-16s%4d %-16s %s", $tag, $., $ARGV, $_)} ' $f
+ continue;;
+ esac
+ case $db in
+ GRTAGS) flag=${eflag}Dxr;;
+ GTAGS) flag=${eflag}Dx;;
+ esac
+ GTAGDBPATH=$dbpath gctags -$flag $f || exit 1
+ done | btreeop -C $dbpath/$db
+done
+exit 0
diff --git a/usr.bin/global/htags/Makefile b/usr.bin/global/htags/Makefile
new file mode 100644
index 0000000..f53639a
--- /dev/null
+++ b/usr.bin/global/htags/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 1.0 (Berkeley) 4/21/96
+
+MAN1= htags.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/htags.pl ${DESTDIR}/usr/bin/htags
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/global/htags/htags.1 b/usr.bin/global/htags/htags.1
new file mode 100644
index 0000000..a887e47
--- /dev/null
+++ b/usr.bin/global/htags/htags.1
@@ -0,0 +1,117 @@
+.\"
+.\" Copyright (c) 1996, 1997 Shigio Yamaguchi. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Shigio Yamaguchi.
+.\" 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 THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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 April 21, 1997
+.Dt HTAGS 1
+.Os BSD 4
+.Sh NAME
+.Nm htags
+.Nd generate hypertext from C and Yacc source code
+.Sh SYNOPSIS
+.Nm htags
+.Op Fl a
+.Op Fl l
+.Op Fl v
+.Op Fl w
+.Op Fl d Ar tagdir
+.Op Fl t Ar title
+.Op Ar dir
+.Sh DESCRIPTION
+.Nm Htags
+makes hypertext from C and Yacc source code using GLOBAL database (GTAGS, GRTAGS).
+.Pp
+In advance of using this command, you must execute
+.Xr gtags 1
+at the root directory of the source tree.
+Then you can execute
+.Nm htags
+at the same place.
+.Nm Htags
+makes HTML directory and generate hypertext in it.
+.Pp
+You can start browsing from 'HTML/index.html'.
+Once hypertext generated, you can move it anywhere and browse it
+by any browsers.
+.Pp
+.br
+.Bl -tag -width Ds
+.It Fl a
+make an alphabetical function index. It's suitable for large project.
+.It Fl l
+make name tag(<A NAME=lno>) for each line so that outer hypertext
+can point any line of this hypertext.
+By default, make it only for lines which have referred object.
+.It Fl v
+verbose mode.
+.It Fl w
+print warning message.
+.It Fl d Ar tagdir
+the directory in which GTAGS and GRTAGS exist. Default is current directory.
+.It Fl t Ar title
+Tile of this hypertext. Default is the last conponent of current path.
+.It Ar dir
+the directory in which hypertext generated. Default is current directory.
+.Sh EXAMPLES
+ % cd /usr/src/sys
+ # gtags -se
+ # htags -vat 'Welcom to FreeBSD kernel source tour!'
+ % lynx HTML/index.html
+.Sh FILES
+.Bl -tag -width tags -compact
+.It Pa HTML/index.html
+Index file.
+.It Pa GTAGS
+tags file for function definitions.
+.It Pa GRTAGS
+tags file for function references.
+.El
+.Sh ENVIRONMENT
+The following environment variables affect the execution of htags.
+.Pp
+.Bl -tag -width indent
+.It Ev TMPDIR
+If this variable is set, its value is used as the directory to make temporary file.
+Default is /tmp.
+.Sh DIAGNOSTICS
+.Nm Htags
+exits with a value of 1 if an error occurred, 0 otherwise.
+.Sh SEE ALSO
+.Xr btreeop 1 ,
+.Xr gctags 1 ,
+.Xr global 1 ,
+.Xr gtags 1 .
+.Sh BUG
+Generated hypertext is VERY LARGE. In advance, check the space of your disk.
+.Sh AUTHORS
+Shigio Yamaguchi (shigio@wafu.netgate.net)
+.Sh HISTORY
+The
+.Nm
+command appeared in FreeBSD 2.2.
diff --git a/usr.bin/global/htags/htags.pl b/usr.bin/global/htags/htags.pl
new file mode 100755
index 0000000..377a41c
--- /dev/null
+++ b/usr.bin/global/htags/htags.pl
@@ -0,0 +1,1082 @@
+#!/usr/bin/perl
+#
+# Copyright (c) 1996, 1997 Shigio Yamaguchi. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Shigio Yamaguchi.
+# 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 THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# htags.pl 21-Apr-97
+#
+$com = $0;
+$com =~ s/.*\///;
+$usage = "usage: $com [-a][-l][-v][-w][-t title][-d tagdir][dir]";
+#-------------------------------------------------------------------------
+# CONFIGURATION
+#-------------------------------------------------------------------------
+# columns of line number
+$ncol = 4;
+# font
+$comment_begin = '<I><FONT COLOR=green>'; # /* ... */
+$comment_end = '</FONT></I>';
+$sharp_begin = '<FONT COLOR=darkred>'; # #define, #include or so on
+$sharp_end = '</FONT>';
+$brace_begin = '<FONT COLOR=blue>'; # { ... }
+$brace_end = '</FONT>';
+$reserved_begin = '<B>'; # if, while, for or so on
+$reserved_end = '</B>';
+# reserved words
+$reserved_words = "auto|break|case|char|continue|default|do|double|else|extern|float|for|goto|if|int|long|register|return|short|sizeof|static|struct|switch|typedef|union|unsigned|void|while";
+# temporary directory
+$tmp = '/tmp';
+if (defined($ENV{'TMPDIR'}) && -d $ENV{'TMPDIR'}) {
+ $tmp = $ENV{'TMPDIR'};
+}
+#-------------------------------------------------------------------------
+# DEFINITION
+#-------------------------------------------------------------------------
+# unit for a path
+$SEP = ' '; # source file path must not include $SEP charactor
+$ESCSEP = &escape($SEP);
+$SRCS = 'S';
+$DEFS = 'D';
+$REFS = 'R';
+$FILES = 'files';
+$FUNCS = 'funcs';
+#-------------------------------------------------------------------------
+# JAVASCRIPT PARTS
+#-------------------------------------------------------------------------
+# escaped angle
+$langle = sprintf("unescape('%s')", &escape('<'));
+$rangle = sprintf("unescape('%s')", &escape('>'));
+# frame name
+$f_mains = 'mains'; # for main view
+$f_funcs = 'funcs'; # for function index
+$f_files = 'files'; # for file index
+$begin_script="<SCRIPT LANGUAGE=javascript>\n<!--\n";
+$end_script="<!-- end of script -->\n</SCRIPT>\n";
+$defaultview=
+ "// if your browser doesn't support javascript, write a BASE tag statically.\n" .
+ "if (parent.frames.length)\n" .
+ " document.write($langle+'BASE TARGET=$f_mains'+$rangle)\n";
+$rewrite_href_funcs =
+ "// IE3.0 seems to be not able to treat following code.\n" .
+ "if (parent.frames.length && parent.$f_funcs == self) {\n" .
+ " document.links[0].href = '../funcs.html';\n" .
+ " document.links[document.links.length - 1].href = '../funcs.html';\n" .
+ "}\n";
+$rewrite_href_files =
+ "// IE3.0 seems to be not able to treat following code.\n" .
+ "if (parent.frames.length && parent.$f_files == self) {\n" .
+ " document.links[0].href = '../files.html';\n" .
+ " document.links[document.links.length - 1].href = '../files.html';\n" .
+ "}\n";
+#-------------------------------------------------------------------------
+# UTIRITIES
+#-------------------------------------------------------------------------
+sub getcwd {
+ local($dir) = `/bin/pwd`;
+ chop($dir);
+ $dir;
+}
+sub date {
+ local($date) = `date`;
+ chop($date);
+ $date;
+}
+sub error {
+ local($msg) = @_;
+ &clean();
+ die($msg);
+}
+sub clean {
+ &anchor'finish();
+ &cache'close();
+}
+sub escape {
+ local($c) = @_;
+ '%' . sprintf("%x", ord($c));
+}
+sub usable {
+ local($com) = @_;
+
+ foreach $path (split(/:/, $ENV{'PATH'})) {
+ if (-x "$path/$com") {
+ return 1;
+ }
+ }
+ return 0;
+}
+#-------------------------------------------------------------------------
+# PROCESS START
+#-------------------------------------------------------------------------
+#
+# options check
+#
+$aflag = $lflag = $vflag = $wflag = $sflag = '';# $sflag is set internally
+while ($ARGV[0] =~ /^-/) {
+ $opt = shift;
+ if ($opt =~ /[^-alvwtd]/) {
+ print STDERR "$usage\n";
+ exit 1;
+ }
+ if ($opt =~ /a/) { $aflag = 1; }
+ if ($opt =~ /l/) { $lflag = 1; }
+ if ($opt =~ /v/) { $vflag = 1; }
+ if ($opt =~ /w/) { $wflag = 1; }
+ if ($opt =~ /t/) {
+ $opt = shift;
+ last if ($opt eq '');
+ $title = $opt;
+ } elsif ($opt =~ /d/) {
+ $opt = shift;
+ last if ($opt eq '');
+ $dbpath = $opt;
+ }
+}
+if (!$title) {
+ @cwd = split('/', &getcwd);
+ $title = $cwd[$#cwd];
+}
+if (!$dbpath) {
+ $dbpath = '.';
+}
+unless (-r "$dbpath/GTAGS" && -r "$dbpath/GRTAGS") {
+ &error("GTAGS and GRTAGS not found. please type 'gtags[RET]'\n");
+}
+#
+# recognize format version
+# if version record is not found, it's assumed version 1.
+#
+ $support_version = 1; # understand this format version
+#
+open(GTAGS, "btreeop -K ' __.VERSION' $dbpath/GTAGS |") || die("$com: GTAGS not found.\n");
+$rec = <GTAGS>;
+close(GTAGS);
+if ($rec =~ /^ __\.VERSION[ \t]+([0-9]+)$/) {
+ $format_version = $1;
+} else {
+ $format_version = 1;
+}
+if ($format_version != $support_version) {
+ die("$com: GTAGS format version unmatched. Please remake it.\n");
+}
+#
+# check directories
+#
+$html = &getcwd() . '/HTML';
+if ($ARGV[0]) {
+ $cwd = &getcwd();
+ unless (-w $ARGV[0]) {
+ &error("$ARGV[0] is not writable directory.\n");
+ }
+ chdir($ARGV[0]) || &error("directory $ARGV[0] not found.\n");
+ $html = &getcwd() . '/HTML';
+ chdir($cwd) || &error("cannot return directory.\n");
+}
+#
+# set sflag if *.[sS] are included.
+#
+open(CHECK, "btreeop $dbpath/GTAGS |") || &error("btreeop $dbpath/GTAGS failed.\n");
+while (<CHECK>) {
+ local($tag, $lno, $filename) = split;
+ if ($filename =~ /\.[sS]$/) {
+ $'sflag = 1;
+ last;
+ }
+}
+close(CHECK);
+#-------------------------------------------------------------------------
+# MAKE FILES
+#-------------------------------------------------------------------------
+# HTML/help.html ... help file (2)
+# HTML/funcs.html ... function index (3)
+# HTML/$FUNCS/* ... function index (3)
+# HTML/$REFS/* ... referencies (4)
+# HTML/$DEFS/* ... definitions (4)
+# HTML/files.html ... file index (5)
+# HTML/$FILES/* ... file index (5)
+# HTML/index.html ... index file (6)
+# HTML/mains.html ... main index (7)
+# HTML/$SRCS/ ... source files (8)
+#-------------------------------------------------------------------------
+print STDERR "[", &date, "] ", "Htags started\n" if ($vflag);
+#
+# (1) make directories
+#
+print STDERR "[", &date, "] ", "(1) making directories ...\n" if ($vflag);
+mkdir($html, 0777) || &error("cannot make directory <$html>.\n") if (! -d $html);
+foreach $d ($SRCS, $REFS, $DEFS, $FILES, $FUNCS) {
+ mkdir("$html/$d", 0775) || &error("cannot make HTML directory\n") if (! -d "$html/$d");
+}
+#
+# (2) make help file
+#
+print STDERR "[", &date, "] ", "(2) making help.html ...\n" if ($vflag);
+&makehelp("$html/help.html");
+#
+# (3) make function index (funcs.html and $FUNCS/*)
+# PRODUCE @funcs
+#
+print STDERR "[", &date, "] ", "(3) making function index ...\n" if ($vflag);
+$func_total = &makefuncindex("$html/funcs.html");
+print STDERR "Total $func_total functions.\n" if ($vflag);
+#
+# (4) make function entries ($DEFS/* and $REFS/*)
+# MAKING TAG CACHE
+#
+print STDERR "[", &date, "] ", "(4) making duplicate entries ...\n" if ($vflag);
+sub suddenly { &clean(); exit 1}
+$SIG{'INT'} = 'suddenly';
+$SIG{'QUIT'} = 'suddenly';
+$SIG{'TERM'} = 'suddenly';
+&cache'open(100000);
+$func_total = &makedupindex($func_total);
+print STDERR "Total $func_total functions.\n" if ($vflag);
+#
+# (5) make file index (files.html and $FILES/*)
+# PRODUCE @files
+#
+print STDERR "[", &date, "] ", "(5) making file index ...\n" if ($vflag);
+$file_total = &makefileindex("$html/files.html");
+print STDERR "Total $file_total files.\n" if ($vflag);
+#
+# [#] make a common part for mains.html and index.html
+# USING @funcs @files
+#
+print STDERR "[", &date, "] ", "(#) making a common part ...\n" if ($vflag);
+$index = &makecommonpart($title);
+#
+# (6)make index file (index.html)
+#
+print STDERR "[", &date, "] ", "(6) making index file ...\n" if ($vflag);
+&makeindex("$html/index.html", $title, $index);
+#
+# (7) make main index (mains.html)
+#
+print STDERR "[", &date, "] ", "(7) making main index ...\n" if ($vflag);
+&makemainindex("$html/mains.html", $index);
+#
+# (#) make anchor database
+#
+print STDERR "[", &date, "] ", "(#) making temporary database ...\n" if ($vflag);
+&anchor'create();
+#
+# (8) make HTML files ($SRCS/*)
+# USING TAG CACHE
+#
+print STDERR "[", &date, "] ", "(8) making hypertext from source code ...\n" if ($vflag);
+&makehtml($file_total);
+&clean();
+print STDERR "[", &date, "] ", "Done.\n" if ($vflag);
+exit 0;
+#-------------------------------------------------------------------------
+# SUBROUTINES
+#-------------------------------------------------------------------------
+#
+# makehelp: make help file
+#
+sub makehelp {
+ local($file) = @_;
+
+ open(HELP, ">$file") || &error("cannot make help file.\n");
+ print HELP "<HTML>\n<HEAD><TITLE>HELP</TITLE></HEAD>\n<BODY>\n";
+ print HELP "<H2>Usage of Links</H2>\n";
+ print HELP "<PRE>/* [&lt;][&gt;][^][v] [top][bottom][index][help] */</PRE>\n";
+ print HELP "<DL>\n";
+ print HELP "<DT>[&lt;]<DD>Previous function.\n";
+ print HELP "<DT>[&gt;]<DD>Next function.\n";
+ print HELP "<DT>[^]<DD>First function in this file.\n";
+ print HELP "<DT>[v]<DD>Last function in this file.\n";
+ print HELP "<DT>[top]<DD>Top of this file.\n";
+ print HELP "<DT>[bottom]<DD>Bottom of this file.\n";
+ print HELP "<DT>[index]<DD>Return to index page (mains.html).\n";
+ print HELP "<DT>[help]<DD>You are seeing now.\n";
+ print HELP "</DL>\n";
+ print HELP "</BODY>\n</HTML>\n";
+ close(HELP);
+}
+#
+# makefuncindex: make function index (including alphabetic index)
+#
+# i) file function index file
+# go) @funcs
+#
+sub makefuncindex {
+ local($file) = @_;
+ local($count) = 0;
+
+ open(FUNCTIONS, ">$file") || &error("cannot make function index <$file>.\n");
+ print FUNCTIONS "<HTML>\n<HEAD><TITLE>FUNCTION INDEX</TITLE>\n";
+ print FUNCTIONS "$begin_script$defaultview$end_script</HEAD>\n<BODY>\n";
+ print FUNCTIONS "<H2>FUNCTION INDEX</H2>\n";
+ print FUNCTIONS "<OL>\n" if (!$aflag);
+ local($old) = select(FUNCTIONS);
+ open(TAGS, "btreeop $dbpath/GTAGS | awk '{print \$1}' | sort | uniq |") || &error("btreeop $dbpath/GTAGS failed.\n");
+ local($alpha) = '';
+ @funcs = (); # [A][B][C]...
+ while (<TAGS>) {
+ $count++;
+ chop;
+ local($tag) = $_;
+ print STDERR " [$count] adding $tag\n" if ($vflag);
+ if ($aflag && $alpha ne substr($tag, 0, 1)) {
+ if ($alpha) {
+ print ALPHA "</OL>\n";
+ print ALPHA "<A HREF=../mains.html TARGET=_self>[index]</A>\n";
+ print ALPHA "$begin_script$rewrite_href_funcs$end_script";
+ print ALPHA "</BODY>\n</HTML>\n";
+ close(ALPHA);
+ }
+ $alpha = substr($tag, 0, 1);
+ push(@funcs, "<A HREF=$FUNCS/$alpha.html TARGET=_self>[$alpha]</A>\n");
+ open(ALPHA, ">$html/$FUNCS/$alpha.html") || &error("cannot make alphabetical function index.\n");
+ print ALPHA "<HTML>\n<HEAD><TITLE>$alpha</TITLE>\n";
+ print ALPHA "$begin_script$defaultview$end_script";
+ print ALPHA "</HEAD>\n<BODY>\n<H2>[$alpha]</H2>\n";
+ print ALPHA "<A HREF=../mains.html TARGET=_self>[index]</A>\n";
+ print ALPHA "<OL>\n";
+ select(ALPHA);
+ }
+ open(LIST, "btreeop -K $tag $dbpath/GTAGS |") || &error("btreeop -K $tag $dbpath/GTAGS failed.\n");;
+ local($line1, $line2);
+ if ($line1 = <LIST>) {
+ $line2 = <LIST>;
+ }
+ close(LIST);
+ if ($line2) {
+ print "<LI><A HREF=", ($aflag) ? "../" : "", "D/$tag.html>$tag</A>\n";
+ } else {
+ local($nouse, $lno, $filename) = split(/[ \t]+/, $line1);
+ $nouse = ''; # to make perl quiet
+ $filename =~ s/^\.\///;
+ $filename =~ s/\//$ESCSEP/g;
+ print "<LI><A HREF=", ($aflag) ? "../" : "", "$SRCS/$filename.html#$lno>$tag</A>\n";
+ }
+ close(LIST);
+ }
+ close(TAGS);
+ select($old);
+ if ($aflag) {
+ print ALPHA "</OL>\n";
+ print ALPHA "<A HREF=../mains.html TARGET=_self>[index]</A>\n";
+ print ALPHA "$begin_script$rewrite_href_funcs$end_script";
+ print ALPHA "</BODY>\n</HTML>\n";
+ close(ALPHA);
+
+ print FUNCTIONS @funcs;
+ }
+ print FUNCTIONS "</OL>\n" if (!$aflag);
+ print FUNCTIONS "</BODY>\n</HTML>\n";
+ close(FUNCTIONS);
+ $count;
+}
+#
+# makedupindex: make duplicate entries index ($DEFS/* and $REFS/*)
+#
+# i) $total functions total
+# r) $count
+#
+sub makedupindex {
+ local($total) = @_;
+ local($count) = 0;
+
+ open(TAGS, "btreeop $dbpath/GTAGS | awk '{print \$1}' | sort | uniq |") || &error("btreeop $dbpath/GTAGS failed.\n");
+ while (<TAGS>) {
+ $count++;
+ chop;
+ local($tag) = $_;
+ print STDERR " [$count/$total] adding $tag\n" if ($vflag);
+ foreach $db ('GTAGS', 'GRTAGS') {
+ open(LIST, "btreeop -K $tag $dbpath/$db | sort +0b -1 +2b -3 +1n -2|") || &error("btreeop -K $tag $dbpath/$db failed.\n");;
+ local($line1, $line2);
+ if ($line1 = <LIST>) {
+ $line2 = <LIST>;
+ }
+ &cache'put($db, $tag, ($line2) ? '' : $line1) if ($line1);
+ if ($line2) { # two or more entries exist
+ local($type) = ($db eq 'GTAGS') ? $'DEFS : $'REFS;
+ open(FILE, ">$html/$type/$tag.html") || &error("cannot make file <$html/$type/$tag.html>.\n");
+ print FILE "<HTML>\n<HEAD><TITLE>$tag</TITLE></HEAD>\n<BODY>\n";
+ print FILE "<PRE>\n";
+ for (;;) {
+ if ($line1) {
+ $_ = $line1;
+ $line1 = '';
+ } elsif ($line2) {
+ $_ = $line2;
+ $line2 = '';
+ } elsif (!($_ = <LIST>)) {
+ last;
+ }
+ s/\.\///;
+ s/&/&amp;/g;
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ local($nouse, $lno, $filename) = split;
+ $nouse = ''; # to make perl quiet
+ $filename =~ s/\//$ESCSEP/g;
+ s/^$tag/<A HREF=..\/$SRCS\/$filename.html#$lno>$tag<\/A>/;
+ print FILE;
+ }
+ print FILE "</PRE>\n</BODY>\n</HTML>\n";
+ close(FILE);
+ }
+ close(LIST);
+ }
+ }
+ close(TAGS);
+ $count;
+}
+#
+# makefileindex: make file index
+#
+# i) file name
+# go) @files
+#
+sub makefileindex {
+ local($file) = @_;
+ local($count) = 0;
+
+ open(FILES, ">$file") || &error("cannot make file <$file>.\n");
+ print FILES "<HTML>\n<HEAD><TITLE>FILES</TITLE>\n";
+ print FILES "$begin_script$defaultview$end_script";
+ print FILES "</HEAD>\n<BODY>\n<H2>FILE INDEX</H2>\n";
+ print FILES "<OL>\n";
+ local($old) = select(FILES);
+ open(FIND, "find . -name '*.[chysS]' -print | sort |") || &error("cannot exec find.\n");
+ local($lastdir) = '';
+ @files = ();
+ while (<FIND>) {
+ $count++;
+ chop;
+ s/^\.\///;
+ next if /(y\.tab\.c|y\.tab\.h)$/;
+ next if (!$'sflag && /\.[sS]$/);
+ local($filename) = $_;
+ print STDERR " [$count] adding $filename\n" if ($vflag);
+ local($dir);
+ if (index($filename, '/') >= 0) {
+ @split = split('/');
+ $dir = $split[0];
+ } else {
+ $dir = '';
+ }
+ #if ($dir && $dir ne $lastdir) {
+ if ($dir ne $lastdir) {
+ if ($lastdir) {
+ print DIR "</OL>\n";
+ print DIR "<A HREF=../mains.html TARGET=_self>[index]</A>\n";
+ print DIR "$begin_script$rewrite_href_files$end_script";
+ print DIR "</BODY>\n</HTML>\n";
+ close(DIR);
+ }
+ if ($dir) {
+ push(@files, "<LI><A HREF=$FILES/$dir.html TARGET=_self>$dir/</A>\n");
+ open(DIR, ">$html/$FILES/$dir.html") || &error("cannot make directory index.\n");
+ print DIR "<HTML>\n<HEAD><TITLE>$dir/</TITLE>\n";
+ print DIR "$begin_script$defaultview$end_script";
+ print DIR "</HEAD>\n<BODY>\n<H2>$dir/</H2>\n";
+ print DIR "<A HREF=../mains.html TARGET=_self>[index]</A>\n";
+ print DIR "<OL>\n";
+ }
+ $lastdir = $dir;
+ }
+ local($path) = $filename;
+ $path =~ s/\//$ESCSEP/g;
+ if ($dir eq '') {
+ push(@files, "<LI><A HREF=", ($dir) ? "../" : "", "$SRCS/$path.html>$filename</A>\n");
+ } else {
+ print DIR "<LI><A HREF=../$SRCS/$path.html>$filename</A>\n";
+ }
+ }
+ close(FIND);
+ select($old);
+ if ($lastdir) {
+ print DIR "</OL>\n";
+ print DIR "<A HREF=../mains.html TARGET=_self>[index]</A>\n";
+ print DIR "$begin_script$rewrite_href_files$end_script";
+ print DIR "</BODY>\n</HTML>\n";
+ close(DIR);
+ }
+ print FILES @files;
+ print FILES "</OL>\n";
+ print FILES "</BODY>\n</HTML>\n";
+ close(FILES);
+
+ $count;
+}
+#
+# makecommonpart: make a common part for mains.html and index.html
+#
+# gi) @files
+# gi) @funcs
+#
+sub makecommonpart {
+ local($title) = @_;
+ local($index) = '';
+
+ $index .= "<H1><FONT COLOR=#cc0000>$title</FONT></H1>\n";
+ $index .= "<P ALIGN=right>";
+ $index .= "Last updated " . &date . "<BR>\n";
+ $index .= "This hypertext was generated by <A HREF=http://wafu.netgate.net/tama/unix/indexe.html#global TARGET=_top>GLOBAL</A>.<BR>\n";
+ $index .= "$begin_script";
+ $index .= "if (parent.frames.length && parent.$f_mains == self)\n";
+ $index .= " document.write($langle+'A HREF=mains.html TARGET=_top'+$rangle+'[No frame version is here.]'+$langle+'/A'+$rangle)\n";
+ $index .= "$end_script";
+ $index .= "</P>\n<HR>\n";
+ $index .= "<H2>MAINS</H2>\n";
+ $index .= "<PRE>\n";
+ open(PIPE, "btreeop -K main $dbpath/GTAGS | sort +0b -1 +2b -3 +1n -2 |") || &error("btreeop -K main $dbpath/GTAGS failed.\n");
+ while (<PIPE>) {
+ local($nouse, $lno, $filename) = split;
+ $nouse = ''; # to make perl quiet
+ $filename =~ s/^\.\///;
+ $filename =~ s/\//$ESCSEP/g;
+ s/(main)/<A HREF=$SRCS\/$filename.html#$lno>$1<\/A>/;
+ $index .= $_;
+ }
+ close(PIPE);
+ $index .= "</PRE>\n<HR>\n<H2>FUNCTIONS</H2>\n";
+ if ($aflag) {
+ foreach $f (@funcs) {
+ $index .= $f;
+ }
+ } else {
+ $index .= "<PRE><A HREF=funcs.html>function index</A></PRE>\n";
+ }
+ $index .= "<HR>\n<H2>FILES</H2>\n";
+ $index .= "<OL>\n";
+ foreach $f (@files) {
+ $index .= $f;
+ }
+ $index .= "</OL>\n<HR>\n";
+ $index;
+}
+#
+# makeindex: make index file
+#
+# i) $file file name
+# i) $title title of index file
+# i) $index common part
+#
+sub makeindex {
+ local($file, $title, $index) = @_;
+
+ open(FRAME, ">$file") || &error("cannot open file <$file>.\n");
+ print FRAME "<HTML>\n<HEAD><TITLE>$title</TITLE></HEAD>\n";
+ print FRAME "<FRAMESET COLS='200,*'>\n";
+ print FRAME "<NOFRAME>\n$index</NOFRAME>\n";
+ print FRAME "<FRAMESET ROWS='50%,50%'>\n";
+ print FRAME "<FRAME NAME=$f_funcs SRC=funcs.html>\n";
+ print FRAME "<FRAME NAME=$f_files SRC=files.html>\n";
+ print FRAME "</FRAMESET>\n";
+ print FRAME "<FRAME NAME=$f_mains SRC=mains.html>\n";
+ print FRAME "</FRAMESET>\n";
+ print FRAME "</HTML>\n";
+ close(FRAME);
+}
+#
+# makemainindex: make main index
+#
+# i) $file file name
+# i) $index common part
+#
+sub makemainindex {
+ local($file, $index) = @_;
+
+ open(INDEX, ">$file") || &error("cannot create file <$file>.\n");
+ print INDEX "<HTML>\n<HEAD><TITLE>MAINS</TITLE></HEAD>\n";
+ print INDEX "<BODY>\n$index</BODY>\n</HTML>\n";
+ close(INDEX);
+}
+#
+# makehtml: make html files
+#
+sub makehtml {
+ local($total) = @_;
+ local($count) = 0;
+
+ open(FIND, "find . -name '*.[chysS]' -print|") || &error("cannot exec find.\n");
+ while (<FIND>) {
+ $count++;
+ chop;
+ s/^\.\///;
+ next if /y\.tab\.c|y\.tab\.h/;
+ next if (!$'sflag && /\.[sS]$/);
+ local($path) = $_;
+ $path =~ s/\//$SEP/g;
+ print STDERR " [$count/$total] converting $_\n" if ($vflag);
+ &convert'src2html($_, "$html/$SRCS/$path.html");
+ }
+ close(FIND);
+}
+#=========================================================================
+# CONVERT PACKAGE
+#=========================================================================
+package convert;
+#
+# src2html: convert source code into HTML
+#
+# i) $file source file - Read from
+# i) $html HTML file - Write to
+#
+sub src2html {
+ local($file, $html) = @_;
+ local($ncol) = $'ncol;
+ local($expand) = &'usable('expand') ? 'expand' : 'cat';
+
+ open(HTML, ">$html") || &'error("cannot create file <$html>.\n");
+ local($old) = select(HTML);
+ #
+ # load tags belonging to this file.
+ #
+ $file =~ s/^\.\///;
+ &anchor'load($file);
+ open(C, "$expand '$file' |") || &'error("cannot open file <$file>.\n");
+ #
+ # print the header
+ #
+ print "<HTML>\n<HEAD><TITLE>$file</TITLE></HEAD>\n";
+ print "<BODY><A NAME=TOP><H2>$file</H2>\n";
+ print &link_format(&anchor'getlinks(0));
+ print "\n<HR>\n";
+ print "<H2>FUNCTIONS</H2>\n";
+ print "This source file includes following functions.\n";
+ print "<OL>\n";
+ local($lno, $tag, $type);
+ for (($lno, $tag, $type) = &anchor'first(); $lno; ($lno, $tag, $type) = &anchor'next()) {
+ print "<LI><A HREF=#$lno>$tag</A>\n" if ($type eq 'D');
+ }
+ print "</OL>\n";
+ print "<HR>\n";
+ #
+ # print source code
+ #
+ print "<PRE>\n";
+ $INCOMMENT = 0; # initial status is out of comment
+ local($LNO, $TAG, $TYPE) = &anchor'first();
+ while (<C>) {
+ s/\r$//;
+ s/&/&amp;/g; # '<', '>' and '&' are used for HTML tag
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ &protect_line(); # protect quoted char, strings and comments
+ # painting source code
+ s/({|})/$'brace_begin$1$'brace_end/g;
+ $sharp = s/^(#\w+)// ? $1 : ''; # protect macro
+ s/\b($'reserved_words)\b/$'reserved_begin$1$'reserved_end/go if ($sharp ne '#include');
+ s/^/$'sharp_begin$sharp$'sharp_end/ if ($sharp); # recover macro
+
+ local($define_line) = 0;
+ local(@links) = ();
+ local($count) = 0;
+ local($lno_printed) = 0;
+
+ if ($'lflag) {
+ print "<A NAME=$.>";
+ $lno_printed = 1;
+ }
+ for (; int($LNO) == $.; ($LNO, $TAG, $TYPE) = &anchor'next()) {
+ if (!$lno_printed) {
+ print "<A NAME=$.>";
+ $lno_printed = 1;
+ }
+ $define_line = $LNO if ($TYPE eq 'D');
+ $db = ($TYPE eq 'D') ? 'GRTAGS' : 'GTAGS';
+ local($line) = &cache'get($db, $TAG);
+ if (defined($line)) {
+ local($href, $dir);
+ if ($line) {
+ local($nouse, $lno, $filename) = split(/[ \t]+/, $line);
+ $nouse = ''; # to make perl quiet
+ $filename =~ s/^\.\///;
+ $filename =~ s/\//$'ESCSEP/g;
+ $href = "<A HREF=../$'SRCS/$filename.html#$lno>$TAG</A>";
+ } else {
+ $dir = ($TYPE eq 'D') ? $'REFS : $'DEFS;
+ $href = "<A HREF=../$dir/$TAG.html>$TAG</A>";
+ }
+ # set tag marks and push hyperlink into @links
+ if (s/\b$TAG\b/\005$count\005/) {
+ $count++;
+ push(@links, $href);
+ } else {
+ print STDERR "Error: $file $LNO $TAG($TYPE) tag must exist.\n" if ($'wflag);
+ }
+ } else {
+ print STDERR "Warning: $file $LNO $TAG($TYPE) found but not referred.\n" if ($'wflag);
+ }
+ }
+ # implant links
+ local($s);
+ for ($count = 0; @links; $count++) {
+ $s = shift @links;
+ unless (s/\005$count\005/$s/) {
+ print STDERR "Error: $file $LNO $TAG($TYPE) tag must exist.\n" if ($'wflag);
+ }
+ }
+ &unprotect_line();
+ # print a line
+ printf "%${ncol}d ", $.;
+ print;
+ # print hyperlinks
+ if ($define_line && $file !~ /\.h$/) {
+ print ' ' x ($ncol + 1);
+ print &link_format(&anchor'getlinks($define_line));
+ print "\n";
+ }
+ }
+ print "</PRE>\n";
+ print "<HR>\n";
+ print "<A NAME=BOTTOM>\n";
+ print &link_format(&anchor'getlinks(-1));
+ print "\n";
+ print "</BODY>\n</HTML>\n";
+ close(C);
+ close(HTML);
+ select($old);
+
+}
+#
+# protect_line: protect quoted strings
+#
+# io) $_ source line
+#
+# \001 quoted(\) char
+# \002 quoted('') char
+# \003 quoted string
+# \004 comment
+# \032 temporary mark
+#
+sub protect_line {
+ @quoted_char1 = ();
+ while (/(\\.)/) {
+ push(@quoted_char1, $1);
+ s/\\./\001/;
+ }
+ @quoted_char2 = ();
+ while (/('[^']')/) {
+ push(@quoted_char2, $1);
+ s/'[^']'/\002/;
+ }
+ @quoted_strings = ();
+ while (/("[^"]*")/) {
+ push(@quoted_strings, $1);
+ s/"[^"]*"/\003/;
+ }
+ @comments = ();
+ s/^/\032/ if ($INCOMMENT);
+ while (1) {
+ if ($INCOMMENT == 0) {
+ if (/\/\*/) { # start comment
+ s/\/\*/\032\/\*/;
+ $INCOMMENT = 1;
+ } else {
+ last;
+ }
+ } else {
+ # Thanks to Jeffrey Friedl for his code.
+ if (m!\032(/\*)?[^*]*\*+([^/*][^*]*\*+)*/!) {
+ $INCOMMENT = 0;
+ # minimum matching
+ s!\032((/\*)?[^*]*\*+([^/*][^*]*\*+)*/)!\004!;
+ push(@comments, $1);
+ } else {
+ s/\032(.*)$/\004/; # mark comment
+ push(@comments, $1);
+ }
+ last if ($INCOMMENT);
+ }
+ }
+}
+#
+# unprotect_line: recover quoted strings
+#
+# i) $_ source line
+#
+sub unprotect_line {
+ local($s);
+
+ while (@comments) {
+ $s = shift @comments;
+ s/\004/$'comment_begin$s$'comment_end/;
+ }
+ while (@quoted_strings) {
+ $s = shift @quoted_strings;
+ s/\003/$s/;
+ }
+ while (@quoted_char2) {
+ $s = shift @quoted_char2;
+ s/\002/$s/;
+ }
+ while (@quoted_char1) {
+ $s = shift @quoted_char1;
+ s/\001/$s/;
+ }
+}
+#
+# link_format: format hyperlinks.
+#
+# i) (previous, next, first, last, top, bottom)
+#
+sub link_format {
+ local(@tag) = @_;
+ local(@label) = ('&lt;', '&gt;', '^', 'v', 'top', 'bottom');
+
+ local($line) = "$'comment_begin/* ";
+ while ($label = shift @label) {
+ local($tag) = shift @tag;
+ $line .= "<A HREF=#$tag>" if ($tag);
+ $line .= "[$label]";
+ $line .= "</A>" if ($tag);
+ }
+ $line .= "<A HREF=../mains.html>[index]</A>";
+ $line .= "<A HREF=../help.html>[help]</A>";
+ $line .= " */$'comment_end";
+
+ $line;
+}
+
+#=========================================================================
+# ANCHOR PACKAGE
+#=========================================================================
+package anchor;
+#
+# create: create anchors temporary database
+#
+sub create {
+ $ANCH = "$'tmp/ANCH$$";
+ open(ANCH, "| btreeop -C $ANCH") || &'error("btreeop -C $ANCH failed.\n");
+ foreach $db ('GTAGS', 'GRTAGS') {
+ local($type) = ($db eq 'GTAGS') ? 'D' : 'R';
+ open(PIPE, "btreeop $'dbpath/$db |") || &'error("btreeop $'dbpath/$db failed.\n");
+ while (<PIPE>) {
+ local($tag, $lno, $filename) = split;
+ print ANCH "$filename $lno $tag $type\n";
+ }
+ close(PIPE);
+ }
+ close(ANCH);
+}
+#
+# finish: remove anchors database
+#
+sub finish {
+ unlink("$ANCH") if (defined($ANCH));
+}
+#
+# load: load anchors in a file from database
+#
+# i) $file source file
+#
+sub load {
+ local($file) = @_;
+
+ $file = './' . $file if ($file !~ /^\.\//);
+
+ @ANCHORS = ();
+ open(ANCH, "btreeop -K $file $ANCH|") || &'error("btreeop -K $file $ANCH failed.\n");
+$n = 0;
+ while (<ANCH>) {
+ local($filename, $lno, $tag, $type) = split;
+ local($line);
+ # START for DTFLAG
+ # don't refer to macros which is defined in other C source.
+ if ($type eq 'R' && ($line = &cache'get('GTAGS', $tag))) {
+ local($nouse1, $nouse2, $f, $def) = split(/[ \t]+/, $line);
+ if ($f !~ /\.h$/ && $f !~ $filename && $def =~ /^#/) {
+ print STDERR "Information: $filename $lno $tag($type) skipped, because this is a macro which is defined in other C source.\n" if ($'wflag);
+ next;
+ }
+ }
+ # END for DTFLAG
+ push(@ANCHORS, "$lno,$tag,$type");
+ }
+ close(ANCH);
+ local(@keys);
+ foreach (@ANCHORS) {
+ push(@keys, (split(/,/))[0]);
+ }
+ sub compare { $keys[$a] <=> $keys[$b]; }
+ @ANCHORS = @ANCHORS[sort compare 0 .. $#keys];
+ local($c);
+ for ($c = 0; $c < @ANCHORS; $c++) {
+ local($lno, $tag, $type) = split(/,/, $ANCHORS[$c]);
+ if ($type eq 'D') {
+ $FIRST = $lno;
+ last;
+ }
+ }
+ for ($c = $#ANCHORS; $c >= 0; $c--) {
+ local($lno, $tag, $type) = split(/,/, $ANCHORS[$c]);
+ if ($type eq 'D') {
+ $LAST = $lno;
+ last;
+ }
+ }
+}
+#
+# first: get first anchor
+#
+sub first {
+ $CURRENT = 0;
+ local($lno, $tag, $type) = split(/,/, $ANCHORS[$CURRENT]);
+ $CURRENTDEF = $CURRENT if ($type eq 'D');
+
+ ($lno, $tag, $type);
+}
+#
+# next: get next anchor
+#
+sub next {
+ if (++$CURRENT > $#ANCHORS) {
+ return ('', '', '');
+ }
+ local($lno, $tag, $type) = split(/,/, $ANCHORS[$CURRENT]);
+ $CURRENTDEF = $CURRENT if ($type eq 'D');
+
+ ($lno, $tag, $type);
+}
+#
+# getlinks: get links
+#
+# i) linenumber >= 1: line number
+# 0: header, -1: tailer
+# gi) @ANCHORS tag table in current file
+# r) (previous, next, first, last, top, bottom)
+#
+sub getlinks {
+ local($linenumber) = @_;
+ local($prev, $next, $first, $last, $top, $bottom);
+
+ $prev = $next = $first = $last = $top = $bottom = 0;
+ if ($linenumber >= 1) {
+ local($c, $p, $n);
+ if ($CURRENTDEF == 0) {
+ for ($c = 0; $c <= $#ANCHORS; $c++) {
+ local($lno, $tag, $type) = split(/,/, $ANCHORS[$c]);
+ if ($lno == $linenumber && $type eq 'D') {
+ last;
+ }
+ }
+ $CURRENTDEF = $c;
+ } else {
+ for ($c = $CURRENTDEF; $c >= 0; $c--) {
+ local($lno, $tag, $type) = split(/,/, $ANCHORS[$c]);
+ if ($lno == $linenumber && $type eq 'D') {
+ last;
+ }
+ }
+ }
+ $p = $n = $c;
+ while (--$p >= 0) {
+ local($lno, $tag, $type) = split(/,/, $ANCHORS[$p]);
+ if ($type eq 'D') {
+ $prev = $lno;
+ last;
+ }
+ }
+ while (++$n <= $#ANCHORS) {
+ local($lno, $tag, $type) = split(/,/, $ANCHORS[$n]);
+ if ($type eq 'D') {
+ $next = $lno;
+ last;
+ }
+ }
+ }
+ $first = $FIRST if ($linenumber != $FIRST);
+ $last = $LAST if ($linenumber != $LAST);
+ $top = 'TOP' if ($linenumber != 0);
+ $bottom = 'BOTTOM' if ($linenumber != -1);
+ if ($FIRST == $LAST) {
+ $last = '' if ($linenumber == 0);
+ $first = '' if ($linenumber == -1);
+ }
+
+ ($prev, $next, $first, $last, $top, $bottom);
+}
+
+#=========================================================================
+# CACHE PACKAGE
+#=========================================================================
+package cache;
+#
+# open: open tag cache
+#
+# i) size cache size
+# -1: all cache
+# 0: no cache
+# other: sized cache
+#
+sub open {
+ ($cachesize) = @_;
+
+ if ($cachesize == -1) {
+ return;
+ }
+ undef %CACH if defined(%CACH);
+ $cachecount = 0;
+}
+#
+# put: put tag into cache
+#
+# i) $db database name
+# i) $tag tag name
+# i) $line tag line
+#
+sub put {
+ local($db, $tag, $line) = @_;
+ local($label) = ($db eq 'GTAGS') ? 'D' : 'R';
+
+ $cachecount++;
+ if ($cachesize >= 0 && $cachecount > $cachesize) {
+ $CACH = "$'tmp/CACH$$";
+ dbmopen(%CACH, $CACH, 0600) || &'error("make cache database.\n");
+ $cachesize = -1;
+ }
+ $CACH{$label.$tag} = $line;
+}
+#
+# get: get tag from cache
+#
+# i) $db database name
+# i) $tag tag name
+# r) tag line
+#
+sub get {
+ local($db, $tag) = @_;
+ local($label) = ($db eq 'GTAGS') ? 'D' : 'R';
+
+ defined($CACH{$label.$tag}) ? $CACH{$label.$tag} : undef;
+}
+#
+# close: close cache
+#
+sub close {
+ #dbmclose(%CACH);
+ unlink("$CACH.db") if (defined($CACH));
+}
diff --git a/usr.bin/global/systags/Makefile b/usr.bin/global/systags/Makefile
new file mode 100644
index 0000000..49c9806
--- /dev/null
+++ b/usr.bin/global/systags/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 1.0 (Berkeley) 4/21/96
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/systags.sh ${DESTDIR}/usr/bin/systags
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/global/systags/systags.sh b/usr.bin/global/systags/systags.sh
new file mode 100755
index 0000000..e6e85bf
--- /dev/null
+++ b/usr.bin/global/systags/systags.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+#
+# Copyright (c) 1997 Shigio Yamaguchi. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Shigio Yamaguchi.
+# 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 THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# systags.sh 17-Feb-97
+#
+# script to make hypertext of kernel source.
+# supporting FreeBSD and Linux.
+#
+case $1 in
+-n) nflag=1; shift;;
+esac
+case $1 in
+"") dir=.;;
+*) dir=$1;;
+esac
+#
+# get release number from source tree.
+#
+if [ -f conf/newvers.sh ]; then
+ os=FreeBSD
+ release=`awk -F= '/^RELEASE=/ {print $2}' < conf/newvers.sh`
+elif [ -f Makefile ] && grep '^vmlinux:' Makefile >/dev/null; then
+ os=Linux
+ version=`awk -F= '/^VERSION *=/ {print $2}' < Makefile`
+ patchlevel=`awk -F= '/^PATCHLEVEL *=/ {print $2}' < Makefile`
+ sublevel=`awk -F= '/^SUBLEVEL *=/ {print $2}' < Makefile`
+ release=`echo "$version.$patchlevel.$sublevel" | tr -d ' '`
+fi
+#
+# remove old files
+#
+case $nflag in
+1) echo "rm -rf $dir/htags.log $dir/GTAGS $dir/GRTAGS $dir/HTML";;
+*) rm -rf $dir/htags.log $dir/GTAGS $dir/GRTAGS $dir/HTML;;
+esac
+#
+# make global database(GTAGS, GRTAGS).
+#
+case $nflag in
+1) echo "gtags -se $dir";;
+*) gtags -se $dir;;
+esac
+#
+# make hypertext.
+# (please replace this title with a suitable one.)
+#
+case $os$release in
+"") program=`/bin/pwd | sed 's/.*\///'`
+ title="Welcome to $program source tour!";;
+*) title="Welcome to $os $release kernel source tour!";;
+esac
+case $nflag in
+1) echo "htags -vwat '$title' -d $dir $dir > $dir/htags.log 2>&1";;
+*) htags -vwat "$title" -d $dir $dir> $dir/htags.log 2>&1;;
+esac
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..76b5543
--- /dev/null
+++ b/usr.bin/gprof/gprof.1
@@ -0,0 +1,293 @@
+.\" 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 cc 1 ,
+.Xr profil 2 ,
+.Xr clocks 7
+.\" .Xr monitor 3 ,
+.\" .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 3
+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..27808d2
--- /dev/null
+++ b/usr.bin/gprof/gprof.h
@@ -0,0 +1,351 @@
+/*
+ * 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;
+
+#ifdef GPROF4
+typedef int32_t UNIT;
+#else
+typedef u_short UNIT; /* unit of profiling */
+#endif
+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..0b039df
--- /dev/null
+++ b/usr.bin/gprof/printgprof.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 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 >= 10000000 ? "ns/call" : hz >= 10000 ? "us/call" : "ms/call" ,
+ hz >= 10000000 ? "ns/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 >= 10000000)
+ printf( " %8d %8.0f %8.0f " , np -> ncall ,
+ 1e9 * np -> time / hz / np -> ncall ,
+ 1e9 * ( np -> time + np -> childtime ) / hz / np -> ncall );
+ else 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/gprof4/Makefile b/usr.bin/gprof4/Makefile
new file mode 100644
index 0000000..f5c2c83
--- /dev/null
+++ b/usr.bin/gprof4/Makefile
@@ -0,0 +1,14 @@
+# This was cloned from the Makefile for gprof by changing PROG from gprof
+# to gprof4, adding NOMAN and PATH, adding -DGPROF4 to CFLAGS and deleting
+# beforeinstall.
+
+# @(#)Makefile 5.17 (Berkeley) 5/11/90
+
+PROG= gprof4
+NOMAN= noman
+SRCS= gprof.c arcs.c dfn.c lookup.c ${MACHINE}.c hertz.c \
+ printgprof.c printlist.c
+CFLAGS+=-DGPROF4
+.PATH: ${.CURDIR}/../../usr.bin/gprof
+
+.include <bsd.prog.mk>
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..a85c93f
--- /dev/null
+++ b/usr.bin/head/head.1
@@ -0,0 +1,72 @@
+.\" 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 Fl c Ar bytes
+.Op Ar file ...
+.Sh DESCRIPTION
+This filter displays the first
+.Ar count
+lines or
+.Ar bytes
+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..e4a7a58
--- /dev/null
+++ b/usr.bin/head/head.c
@@ -0,0 +1,219 @@
+/*
+ * 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.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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 head_bytes __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 = -1, bytecnt = -1;
+ char *ep;
+
+ obsolete(argv);
+ while ((ch = getopt(argc, argv, "n:c:")) != -1)
+ switch(ch) {
+ case 'c':
+ bytecnt = strtol(optarg, &ep, 10);
+ if (*ep || bytecnt <= 0)
+ err(1, "illegal byte count -- %s", optarg);
+ break;
+ 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 (linecnt != -1 && bytecnt != -1)
+ err(1, "can't combine line and byte counts");
+ if (linecnt == -1 )
+ linecnt = 10;
+ 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;
+ }
+ if (bytecnt == -1)
+ head(fp, linecnt);
+ else
+ head_bytes(fp, bytecnt);
+ (void)fclose(fp);
+ }
+ }
+ else if (bytecnt == -1)
+ head(stdin, linecnt);
+ else
+ head_bytes(stdin, bytecnt);
+
+ exit(eval);
+}
+
+void
+head(fp, cnt)
+ FILE *fp;
+ register int cnt;
+{
+ register int ch;
+
+ while (cnt && (ch = getc(fp)) != EOF) {
+ if (putchar(ch) == EOF)
+ err(1, "stdout: %s", strerror(errno));
+ if (ch == '\n')
+ cnt--;
+ }
+}
+
+void
+head_bytes(fp, cnt)
+ FILE *fp;
+ register int cnt;
+{
+ char buf[4096];
+ register int readlen;
+
+ while (cnt) {
+ if (cnt < sizeof(buf))
+ readlen = cnt;
+ else
+ readlen = sizeof(buf);
+ readlen = fread(buf, sizeof(char), readlen, fp);
+ if (readlen == EOF)
+ break;
+ if (fwrite(buf, sizeof(char), readlen, stdout) != readlen)
+ err(1, "stdout: %s", strerror(errno));
+ cnt -= readlen;
+ }
+}
+
+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..24ad198
--- /dev/null
+++ b/usr.bin/hexdump/Makefile
@@ -0,0 +1,10 @@
+# @(#)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
+MLINKS= hexdump.1 hd.1
+LINKS= ${BINDIR}/hexdump ${BINDIR}/od
+LINKS+= ${BINDIR}/hexdump ${BINDIR}/hd
+
+.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..1fd6d07
--- /dev/null
+++ b/usr.bin/hexdump/hexdump.1
@@ -0,0 +1,343 @@
+.\" 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
+.\" $Id$
+.\"
+.Dd April 18, 1994
+.Dt HEXDUMP 1
+.Os
+.Sh NAME
+.Nm hexdump, hd
+.Nd ascii, decimal, hexadecimal, octal dump
+.Sh SYNOPSIS
+.Nm hexdump
+.Op Fl bcCdovx
+.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 ...
+.Nm hd
+.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 C
+.Em Canonical hex+ASCII display.
+Display the input offset in hexadecimal, followed by sixteen
+space-separated, two column, hexadecimal bytes, followed by the
+same sixteen bytes in %_p format enclosed in ``|'' characters.
+.Pp
+Calling the command
+.Nm hd
+implies this option.
+.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 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 gdb 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..279a68c
--- /dev/null
+++ b/usr.bin/hexdump/hexsyntax.c
@@ -0,0 +1,141 @@
+/*-
+ * 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.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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;
+ if ((p = rindex(argv[0], 'h')) != NULL &&
+ strcmp(p, "hd") == 0) {
+ /* "Canonical" format, implies -C. */
+ add("\"%08.8_Ax\n\"");
+ add("\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" ");
+ add("\" |\" 16/1 \"%_p\" \"|\\n\"");
+ }
+ while ((ch = getopt(argc, argv, "bcCde:f:n:os:vx")) != -1)
+ 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 'C':
+ add("\"%08.8_Ax\n\"");
+ add("\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" ");
+ add("\" |\" 16/1 \"%_p\" \"|\\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: [-bcCdovx] [-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..001b900
--- /dev/null
+++ b/usr.bin/hexdump/od.1
@@ -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.
+.\"
+.\" @(#)od.1 8.1 (Berkeley) 6/6/93
+.\" $Id: od.1,v 1.5 1997/02/22 19:55:07 peter Exp $
+.\"
+.Dd May 27, 1994
+.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
+.Nm 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.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/usr.bin/hexdump/odsyntax.c b/usr.bin/hexdump/odsyntax.c
new file mode 100644
index 0000000..e4c4e72
--- /dev/null
+++ b/usr.bin/hexdump/odsyntax.c
@@ -0,0 +1,265 @@
+/*-
+ * 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.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.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")) != -1)
+ 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..ebccefa
--- /dev/null
+++ b/usr.bin/host/Makefile
@@ -0,0 +1,10 @@
+# $Id$
+
+.include "${.CURDIR}/../../usr.sbin/named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/tools
+.PATH: ${BIND_DIR}/man
+
+PROG= host
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/id/Makefile b/usr.bin/id/Makefile
new file mode 100644
index 0000000..5b90f29
--- /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}${BINDIR}/groups
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/whoami.sh ${DESTDIR}${BINDIR}/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..58204af
--- /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..904f200
--- /dev/null
+++ b/usr.bin/id/groups.sh
@@ -0,0 +1,39 @@
+#!/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.
+#
+# @(#)groups.sh 8.1 (Berkeley) 6/6/93
+# $Id$
+
+PATH=/bin:/usr/bin; export PATH
+
+exec id -Gn -- "$@"
diff --git a/usr.bin/id/id.1 b/usr.bin/id/id.1
new file mode 100644
index 0000000..1d194e9
--- /dev/null
+++ b/usr.bin/id/id.1
@@ -0,0 +1,145 @@
+.\" 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
+.\" $Id: id.1,v 1.2 1997/04/27 08:45:44 jmg Exp $
+.\"
+.Dd June 6, 1993
+.Dt ID 1
+.Os BSD 4.4
+.Sh NAME
+.Nm id
+.Nd return user identity
+.Sh SYNOPSIS
+.Nm
+.Op Ar user
+.Nm
+.Fl G Op Fl n
+.Op Ar user
+.Nm
+.Fl g Op Fl nr
+.Op Ar user
+.Nm
+.Fl p
+.Nm
+.Fl u Op Fl nr
+.Op Ar user
+.Sh DESCRIPTION
+The
+.Nm
+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
+.Dq login .
+The user ID as a name is displayed, preceded by the keyword
+.Dq 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
+.Dq 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
+.Dq rgid .
+The list of groups to which the user belongs is then displayed as names,
+preceded by the keyword
+.Dq 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
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr who 1
+.Sh STANDARDS
+The
+.Nm
+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 appeared in
+.Bx 4.4 .
diff --git a/usr.bin/id/id.c b/usr.bin/id/id.c
new file mode 100644
index 0000000..05face7
--- /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")) != -1)
+ 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..738e346
--- /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..1c24bcc
--- /dev/null
+++ b/usr.bin/id/whoami.sh
@@ -0,0 +1,39 @@
+#!/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.
+#
+# @(#)whoami.sh 8.1 (Berkeley) 6/6/93
+# $Id$
+
+PATH=/bin:/usr/bin; export PATH
+
+exec id -un
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..7f92a23
--- /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 Ns Ar n
+The column in which comments on code start. The default is 33.
+.It Fl cd Ns Ar 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..6cc7bda
--- /dev/null
+++ b/usr.bin/ipcrm/Makefile
@@ -0,0 +1,5 @@
+# $Id$
+
+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..8a3a532
--- /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.3 1997/02/22 19:55:11 peter Exp $
+.\""
+.Dd August 8, 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..717c826
--- /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$
+ */
+
+#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..5e4afcc
--- /dev/null
+++ b/usr.bin/ipcs/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+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..c0e53cb
--- /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.5 1997/02/22 19:55:15 peter 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 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 .
+.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
+and semaphores.
+.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..28db9b0
--- /dev/null
+++ b/usr.bin/ipcs/ipcs.c
@@ -0,0 +1,493 @@
+/*
+ * 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.8 1997/02/22 19:55:18 peter 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")) != -1)
+ 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();
+ }
+
+ /*
+ * Discard setgid privileges if not the running kernel so that bad
+ * guys can't print interesting stuff from kernel memory.
+ */
+ if (namelist != NULL || core != NULL)
+ setgid(getgid());
+
+ 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) * shminfo.shmmni);
+ 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..a1855cf
--- /dev/null
+++ b/usr.bin/join/join.1
@@ -0,0 +1,215 @@
+.\" 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.3 (Berkeley) 4/28/95
+.\"
+.Dd April 28, 1995
+.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 .
+(The argument to
+.Fl a
+must not be preceded by a space; see the
+.Sx COMPATIBILITY
+section.)
+.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.
+(To distinguish between this and
+.Fl a Ar file_number ,
+.Nm join
+currently requires that the latter not include any white space.)
+.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..7b1e779
--- /dev/null
+++ b/usr.bin/join/join.c
@@ -0,0 +1,588 @@
+/*-
+ * 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.6 (Berkeley) 5/4/95";
+#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>
+#include <unistd.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:")) != -1) {
+ 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) {
+ /*
+ * 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));
+
+ /* re-set lastlp in case it moved */
+ if (lastlp != NULL)
+ lastlp = &F->set[F->setcnt - 1];
+ }
+
+ /*
+ * 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.
+ */
+ lp = &F->set[F->setcnt];
+ if (F->setcnt)
+ lastlp = &F->set[F->setcnt - 1];
+ 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 - lp->linealloc);
+ 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..39b6d55
--- /dev/null
+++ b/usr.bin/jot/jot.c
@@ -0,0 +1,398 @@
+/*-
+ * 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>
+#include <unistd.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);
+ if (s == -1.0)
+ srandomdev();
+ else
+ srandom((unsigned long) s);
+ for (*i = 1; *i <= reps || infinity; (*i)++) {
+ *y = (double) random() / LONG_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])
+ sepstring = *av + 2;
+ else if (!--ac)
+ error("Need string after -s", "");
+ else
+ 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 ? -1.0 : STEP_DEF);
+ mask = 013;
+ break;
+ case 013:
+ if (randomize)
+ begin = BEGIN_DEF;
+ else if (reps == 0)
+ error("Must specify begin if reps == 0", "");
+ else
+ begin = ender - reps * s + s;
+ mask = 0;
+ break;
+ case 014:
+ s = (randomize ? -1.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 = -1.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..99a1471
--- /dev/null
+++ b/usr.bin/kdump/kdump.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.
+.\"
+.\" @(#)kdump.1 8.1 (Berkeley) 6/6/93
+.\" $Id$
+.\"
+.Dd June 6, 1993
+.Dt KDUMP 1
+.Os BSD 4.4
+.Sh NAME
+.Nm kdump
+.Nd display kernel trace data
+.Sh SYNOPSIS
+.Nm
+.Op Fl dnlRT
+.Op Fl f Ar file
+.Op Fl m Ar maxdata
+.Op Fl t Op cnisu
+.Sh DESCRIPTION
+The
+.Nm
+command 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
+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 cnisuw
+See the
+.Fl t
+option of
+.Xr ktrace 1 .
+.El
+.Sh SEE ALSO
+.Xr ktrace 1
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/usr.bin/kdump/kdump.c b/usr.bin/kdump/kdump.c
new file mode 100644
index 0000000..debf08a
--- /dev/null
+++ b/usr.bin/kdump/kdump.c
@@ -0,0 +1,457 @@
+/*-
+ * 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 <locale.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;
+
+ (void) setlocale(LC_CTYPE, "");
+
+ while ((ch = getopt(argc,argv,"f:dlm:nRTt:")) != -1)
+ 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();
+ }
+
+ if (argc > optind)
+ 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;
+ case KTR_USER:
+ ktruser(ktrlen, 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;
+ case KTR_USER:
+ type = "USER";
+ 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 byte%s\n", ktr->ktr_fd,
+ ktr->ktr_rw == UIO_READ ? "read" : "wrote", datalen,
+ datalen == 1 ? "" : "s");
+ 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");
+}
+
+ktruser(len, p)
+ int len;
+ unsigned char *p;
+{
+ (void)printf("%d ", len);
+ while (len--)
+ (void)printf(" %02x", *p++);
+ (void)printf("\n");
+
+}
+
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: kdump [-dnlRT] [-f trfile] [-m maxdata] [-t [cnisuw]]\n");
+ exit(1);
+}
diff --git a/usr.bin/kdump/mkioctls b/usr.bin/kdump/mkioctls
new file mode 100644
index 0000000..8ff0cd6
--- /dev/null
+++ b/usr.bin/kdump/mkioctls
@@ -0,0 +1,41 @@
+# XXX should we use an ANSI cpp?
+# XXX does -I$DESTDIR/usr/include actually work?
+(echo "#include <sys/ioctl.h>"
+ echo "#include <sys/ioctl_compat.h>"
+) | cpp -I$DESTDIR/usr/include -dM | awk '
+BEGIN {
+ print "#include <sys/param.h>"
+ print "#include <sys/queue.h>"
+ print "#include <sys/socket.h>"
+ print "#include <sys/socketvar.h>"
+ print "#include <sys/time.h>"
+ print "#include <net/route.h>"
+ print "#include <net/if.h>"
+ print "#include <netinet/in.h>"
+ print "#include <netinet/ip_mroute.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 "}"
+}
+'
diff --git a/usr.bin/key/Makefile b/usr.bin/key/Makefile
new file mode 100644
index 0000000..c150e63
--- /dev/null
+++ b/usr.bin/key/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 5.6 (Berkeley) 3/5/91
+#
+
+PROG= key
+SRCS= skey.c
+MAN1= key.1
+
+CFLAGS+= -D_SKEY_INTERNAL
+
+DPADD= ${LIBSKEY} ${LIBMD}
+LDADD= -lskey -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/key/key.1 b/usr.bin/key/key.1
new file mode 100644
index 0000000..7399233
--- /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.c b/usr.bin/key/skey.c
new file mode 100644
index 0000000..0196ebe
--- /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:")) != -1){
+ 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..93bfb8a
--- /dev/null
+++ b/usr.bin/keyinfo/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 5.5 (Berkeley) 7/1/90
+
+BINOWN= root
+BINMODE=4555
+
+MAN1= keyinfo.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/keyinfo.pl ${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..9ddea83
--- /dev/null
+++ b/usr.bin/keyinfo/keyinfo.1
@@ -0,0 +1,50 @@
+.ll 6i
+.pl 10.5i
+.\" from: @(#)keyinfo.1 1.1 (Bellcore) 7/20/93
+.\" $Id$
+.\"
+.lt 6.0i
+.TH KEYINFO 1 "26 April 1996"
+.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> `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 DIAGNOSTICS
+.I keyinfo
+exits with status 0 if a key for the requested user has been found,
+else with status 1.
+.SH SEE ALSO
+.BR keyinit(1),
+.BR key(1)
+.SH AUTHOR
+Original command by Phil Karn, Neil M. Haller, John S. Walden.
+Rewritten in Perl by
+.ie t J\(:org \%Wunsch
+.el Joerg Wunsch
+so it can be made setuid, and the S/Key keys file can be read-protected
+from the users.
diff --git a/usr.bin/keyinfo/keyinfo.pl b/usr.bin/keyinfo/keyinfo.pl
new file mode 100644
index 0000000..260a724
--- /dev/null
+++ b/usr.bin/keyinfo/keyinfo.pl
@@ -0,0 +1,27 @@
+#!/usr/bin/suidperl
+#
+# Search /etc/skeykeys for the skey string for this user OR user specified
+# in 1st parameter.
+#
+# $Id$
+#
+
+die "usage: keyinfo [user]\n" unless $#ARGV < 1;
+
+open(K, "/etc/skeykeys") || exit 1;
+
+if ($#ARGV == 0) {
+ $user = $ARGV[0];
+} else {
+ $user = (getpwuid($<))[0];
+}
+
+while (<K>) {
+ ($id, $seq, $serial) = split;
+ if ($id eq $user) {
+ printf "%d %s\n", $seq - 1, $serial;
+ exit 0;
+ }
+}
+exit 1;
+
diff --git a/usr.bin/keyinit/Makefile b/usr.bin/keyinit/Makefile
new file mode 100644
index 0000000..2796a3d
--- /dev/null
+++ b/usr.bin/keyinit/Makefile
@@ -0,0 +1,15 @@
+# @(#)Makefile 5.6 (Berkeley) 3/5/91
+#
+PROG= keyinit
+MAN1= keyinit.1
+SRCS= skeyinit.c
+
+CFLAGS+= -D_SKEY_INTERNAL
+
+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..aba9cee
--- /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 combination 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 su(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..0a739b3
--- /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 key 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/keylogin/Makefile b/usr.bin/keylogin/Makefile
new file mode 100644
index 0000000..971a746
--- /dev/null
+++ b/usr.bin/keylogin/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= keylogin
+SRCS= keylogin.c
+
+MAN1= keylogin.1
+
+LDADD+= -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/keylogin/keylogin.1 b/usr.bin/keylogin/keylogin.1
new file mode 100644
index 0000000..424454982
--- /dev/null
+++ b/usr.bin/keylogin/keylogin.1
@@ -0,0 +1,25 @@
+.\" @(#)keylogin.1 1.5 91/03/11 TIRPC 1.0;
+.\" Copyright (c) 1988 Sun Microsystems, Inc. - All Rights Reserved.
+.\"
+.TH KEYLOGIN 1 "9 September 1987"
+.SH NAME
+keylogin \- decrypt and store secret key
+.SH SYNOPSIS
+.B keylogin
+.SH DESCRIPTION
+.LP
+.B keylogin
+prompts the user for their login password, and uses it to decrypt
+the user's secret key stored in the
+.BR publickey (5)
+database. Once decrypted, the user's key is stored by the local
+key server process
+.BR keyserv (8C)
+to be used by any secure network services, such as
+.SM NFS\s0.
+.SH "SEE ALSO"
+.BR chkey (1),
+.BR login (1),
+.BR publickey (5),
+.BR keyserv (8C),
+.BR newkey (8)
diff --git a/usr.bin/keylogin/keylogin.c b/usr.bin/keylogin/keylogin.c
new file mode 100644
index 0000000..55529c8
--- /dev/null
+++ b/usr.bin/keylogin/keylogin.c
@@ -0,0 +1,80 @@
+/*
+ * 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 or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * 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
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)keylogin.c 1.4 91/03/11 Copyr 1986 Sun Micro";
+#endif
+
+/*
+ * Copyright (C) 1986, Sun Microsystems, Inc.
+ */
+
+/*
+ * Set secret key on local machine
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+
+int
+main()
+{
+ char fullname[MAXNETNAMELEN + 1];
+ struct netstarg netst;
+
+ if (!getnetname(fullname)) {
+ fprintf(stderr, "netname lookup failed -- make sure the ");
+ fprintf(stderr, "system domain name is set.\n");
+ exit(1);
+ }
+
+ if (! getsecretkey(fullname, (char *)&(netst.st_priv_key),
+ getpass("Password:"))) {
+ fprintf(stderr, "Can't find %s's secret key\n", fullname);
+ exit(1);
+ }
+ if (netst.st_priv_key[0] == 0) {
+ fprintf(stderr, "Password incorrect for %s\n", fullname);
+ exit(1);
+ }
+
+ netst.st_pub_key[0] = 0;
+ netst.st_netname = strdup(fullname);
+
+ if (key_setnet(&netst) < 0) {
+ fprintf(stderr, "Could not set %s's secret key\n", fullname);
+ fprintf(stderr, "Maybe the keyserver is down?\n");
+ exit(1);
+ }
+ exit(0);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/keylogout/Makefile b/usr.bin/keylogout/Makefile
new file mode 100644
index 0000000..c5bd6f5
--- /dev/null
+++ b/usr.bin/keylogout/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= keylogout
+SRCS= keylogout.c
+
+MAN1= keylogout.1
+
+LDADD+= -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/keylogout/keylogout.1 b/usr.bin/keylogout/keylogout.1
new file mode 100644
index 0000000..cbc5613
--- /dev/null
+++ b/usr.bin/keylogout/keylogout.1
@@ -0,0 +1,44 @@
+.\" @(#)keylogout.1 1.4 91/03/11 TIRPC 1.0; from 1.3 89/07/26 SMI;
+.TH KEYLOGOUT 1 "15 April 1989"
+.SH NAME
+keylogout \- delete stored secret key
+.SH SYNOPSIS
+.B keylogout
+[
+.B \-f
+]
+.SH DESCRIPTION
+.IX "keylogout command" "" "\fLkeylogout\fR command"
+.LP
+.B keylogout
+deletes the key stored by the key server process
+.BR keyserv (8C)
+to be used by any secure network services, such as
+.SM NFS\s0.
+Further access to the key is revoked,
+however current session keys may remain valid till they expire,
+or are refreshed.
+This option will cause any background jobs that need secure
+.SM RPC
+services to fail, and any scheduled
+.B at
+jobs that need the key to fail.
+Also since only one copy is kept on a machine of the key,
+it is a bad idea to place this in your
+.B .logout
+file since it will affect other sessions on the same machine.
+.SH OPTIONS
+.TP
+.B \-f
+Forget the rootkey.
+This will break secure
+.SM NFS\s0
+if it is done on a server.
+.LP
+.SH "SEE ALSO"
+.BR chkey (1),
+.BR login (1),
+.BR keylogin (1),
+.BR publickey (5),
+.BR keyserv (8C),
+.BR newkey (8)
diff --git a/usr.bin/keylogout/keylogout.c b/usr.bin/keylogout/keylogout.c
new file mode 100644
index 0000000..51c03b1
--- /dev/null
+++ b/usr.bin/keylogout/keylogout.c
@@ -0,0 +1,68 @@
+/*
+ * 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 or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * 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
+ */
+/*
+ * Copyright (C) 1986, Sun Microsystems, Inc.
+ */
+
+/*
+ * unset the secret key on local machine
+ */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+
+int
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ static char secret[HEXKEYBYTES + 1];
+
+ if (geteuid() == 0) {
+ if ((argc != 2 ) || (strcmp(argv[1], "-f") != 0)) {
+ fprintf(stderr,
+"keylogout by root would break all servers that use secure rpc!\n");
+ fprintf(stderr,
+"root may use keylogout -f to do this (at your own risk)!\n");
+ exit(-1);
+ }
+ }
+
+ if (key_setsecret(secret) < 0) {
+ fprintf(stderr, "Could not unset your secret key.\n");
+ fprintf(stderr, "Maybe the keyserver is down?\n");
+ exit(1);
+ }
+ exit(0);
+ /* NOTREACHED */
+}
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..a05c529
--- /dev/null
+++ b/usr.bin/killall/killall.1
@@ -0,0 +1,134 @@
+.\" 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 ALL PROCESSES
+Sending a signal to all processes with uid
+.Nm XYZ
+is already supported by
+.Xr kill 1 .
+So use
+.Xr kill 1
+for this job (e.g. $ kill -TERM -1 or
+as root $ echo kill -TERM -1 | su -m <user>)
+
+
+.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 successfully. 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 procfs 5 .
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.1 .
+It has been modeled 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.
diff --git a/usr.bin/killall/killall.pl b/usr.bin/killall/killall.pl
new file mode 100755
index 0000000..2e8dc66
--- /dev/null
+++ b/usr.bin/killall/killall.pl
@@ -0,0 +1,117 @@
+#!/usr/bin/perl
+#
+# Copyright (c) 1995-1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (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 processes by name
+#
+# $Id$
+
+
+$ENV{'PATH'} = '/bin:/usr/bin'; # security
+$procfs = '/proc';
+$signal = 'SIGTERM'; # default signal for kill
+$debug = 0;
+$match = 0; # 0 match exactly program name
+$show = 0; # do nothings
+
+# see /sys/miscfs/procfs/procfs_status.c
+$PROC_NAME = 0;
+$PROC_EUID = 11;
+$PROC_RUID = 12;
+
+sub usage {
+ $! = 2;
+ die "killall [-?|-help] [-d] [-l] [-m] [-s] [-SIGNAL] procname ...\n";
+}
+
+$id = $<; # real uid of this process / your id
+while ($_ = $ARGV[0], /^-/) {
+ shift @ARGV;
+ if (/^--$/) { $_ = $ARGV[0]; last }
+ elsif (/^-(h|help|\?)$/) { &usage }
+ elsif (/^-[dv]$/) { $debug++ }
+ elsif (/^-l$/) { exec 'kill', '-l' }
+ elsif (/^-m$/) { $match = 1 }
+ elsif (/^-s$/) { $show = 1 }
+ elsif (/^-([a-z][a-z0-9]+|[0-9]+)$/i) { $signal = $1 }
+ elsif (/^-/) { &usage }
+}
+
+&usage if $#ARGV < 0; # no arguments
+die "Maybe $procfs is not mounted\n" unless -e "$procfs/0/status";
+
+while ($program = $ARGV[0]) {
+ shift @ARGV;
+ $thiskill = 0;
+
+ opendir(PROCFS, "$procfs") || die "$procfs $!\n";
+ print " PID EUID RUID COMMAND\n" if $debug > 1;
+
+ # quote meta characters
+ ($programMatch = $program) =~ s/(\W)/\\$1/g;
+
+ 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 %5d %5d %s\n", $pid, $proc[$PROC_EUID],
+ $proc[$PROC_RUID], $proc[$PROC_NAME] if $debug > 1;
+
+ if ( # match program name
+ ($proc[$PROC_NAME] eq $program ||
+ ($match && $proc[$PROC_NAME] =~ /$programMatch/oi)
+ ) &&
+ # id test
+ ($proc[$PROC_EUID] eq $id || # effective uid
+ $proc[$PROC_RUID] eq $id || # real uid
+ !$id)) # root
+ {
+ push(@kill, $pid);
+ $thiskill++;
+ }
+ }
+ close STATUS;
+ }
+ closedir PROCFS;
+
+ # nothing found
+ warn "No matching processes ``$program''\n" unless $thiskill;
+}
+
+# nothing found
+exit(1) if $#kill < 0;
+
+$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..7cfae99
--- /dev/null
+++ b/usr.bin/ktrace/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 1.1 (Berkeley) 6/6/93
+# $Id$
+
+PROG= ktrace
+SRCS= ktrace.c subr.c
+MLINKS= ktrace.1 trace.1 ktrace.1 truss.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ktrace/ktrace.1 b/usr.bin/ktrace/ktrace.1
new file mode 100644
index 0000000..af9e14c
--- /dev/null
+++ b/usr.bin/ktrace/ktrace.1
@@ -0,0 +1,173 @@
+.\" 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
+.\" $Id$
+.\"
+.Dd June 6, 1993
+.Dt KTRACE 1
+.Os BSD 4.4
+.Sh NAME
+.Nm ktrace
+.Nd enable kernel process tracing
+.Sh SYNOPSIS
+.Nm
+.Op Fl aCcdi
+.Op Fl f Ar trfile
+.Op Fl g Ar pgrp
+.Op Fl p Ar pid
+.Op Fl t Ar trstr
+.Nm
+.Op Fl adi
+.Op Fl f Ar trfile
+.Op Fl t Ar trstr
+command
+.Sh DESCRIPTION
+The
+.Nm
+command 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 \&$ ktrace -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 recreating 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
+.It Cm u
+userland traces
+.It Cm w
+context switches
+.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 BUGS
+Only works if
+.Ar file
+is a regular file.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/usr.bin/ktrace/ktrace.c b/usr.bin/ktrace/ktrace.c
new file mode 100644
index 0000000..534b09a
--- /dev/null
+++ b/usr.bin/ktrace/ktrace.c
@@ -0,0 +1,202 @@
+/*-
+ * 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
+#if 0
+static char sccsid[] = "@(#)ktrace.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: ktrace.c,v 1.9 1997/03/15 10:39:12 joerg Exp $";
+#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 <err.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "ktrace.h"
+
+void no_ktrace __P((int));
+void usage __P((void));
+
+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;
+ mode_t omask;
+ struct stat sb;
+
+ clear = NOTSET;
+ append = ops = pidset = inherit = 0;
+ trpoints = DEF_POINTS;
+ tracefile = DEF_TRACEFILE;
+ while ((ch = getopt(argc,argv,"aCcdf:g:ip:t:")) != -1)
+ 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) {
+ warnx("unknown facility in %s", optarg);
+ usage();
+ }
+ break;
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (pidset && *argv || !pidset && !*argv)
+ usage();
+
+ if (inherit)
+ trpoints |= KTRFAC_INHERIT;
+
+ (void)signal(SIGSYS, no_ktrace);
+ 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)
+ err(1, tracefile);
+ exit(0);
+ }
+
+ omask = umask(S_IRWXG|S_IRWXO);
+ if (append) {
+ if ((fd = open(tracefile, O_CREAT | O_WRONLY, DEFFILEMODE)) < 0)
+ err(1, tracefile);
+ if (fstat(fd, &sb) != 0 || sb.st_uid != getuid())
+ errx(1, "Refuse to append to %s not owned by you.",
+ tracefile);
+ } else {
+ if (unlink(tracefile) == -1 && errno != ENOENT)
+ err(1, "unlink %s", tracefile);
+ if ((fd = open(tracefile, O_CREAT | O_EXCL | O_WRONLY,
+ DEFFILEMODE)) < 0)
+ err(1, tracefile);
+ }
+ (void)umask(omask);
+ (void)close(fd);
+
+ if (*argv) {
+ if (ktrace(tracefile, ops, trpoints, getpid()) < 0)
+ err(1, tracefile);
+ execvp(argv[0], &argv[0]);
+ err(1, "exec of '%s' failed", argv[0]);
+ }
+ else if (ktrace(tracefile, ops, trpoints, pid) < 0)
+ err(1, tracefile);
+ exit(0);
+}
+
+rpid(p)
+ char *p;
+{
+ static int first;
+
+ if (first++) {
+ warnx("only one -g or -p flag is permitted.");
+ usage();
+ }
+ if (!*p) {
+ warnx("illegal process id.");
+ usage();
+ }
+ return(atoi(p));
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"usage:\tktrace [-aCcid] [-f trfile] [-g pgid] [-p pid] [-t [cnisuv]\n\tktrace [-aCcid] [-f trfile] [-t [cnisuw] command\n");
+ exit(1);
+}
+
+void
+no_ktrace(sig)
+ int sig;
+{
+ (void)fprintf(stderr,
+"error:\tktrace() system call not supported in the running kernel\n\tre-compile kernel with 'options KTRACE'\n");
+ exit(1);
+}
diff --git a/usr.bin/ktrace/ktrace.h b/usr.bin/ktrace/ktrace.h
new file mode 100644
index 0000000..9f6431a
--- /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 | KTRFAC_USER)
+
+#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..8af26ba
--- /dev/null
+++ b/usr.bin/ktrace/subr.c
@@ -0,0 +1,116 @@
+/*-
+ * 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
+#if 0
+static char sccsid[] = "@(#)subr.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#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 'u':
+ facs |= KTRFAC_USER;
+ 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..7c9f8c3
--- /dev/null
+++ b/usr.bin/kzip/Makefile
@@ -0,0 +1,6 @@
+# $Id$
+
+PROG= kzip
+MAN8= kzip.8
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/kzip/kzip.8 b/usr.bin/kzip/kzip.8
new file mode 100644
index 0000000..9989f1e
--- /dev/null
+++ b/usr.bin/kzip/kzip.8
@@ -0,0 +1,73 @@
+.\"
+.\" Copyright (c) 1996 David E. O'Brien
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (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 August 15, 1996
+.Os
+.Dt KZIP 8
+.Sh NAME
+.Nm kzip
+.Nd compresses kernels
+.Sh SYNOPSIS
+.Nm kzip
+.Op Fl v
+.Op Fl l Ar loadaddr
+.Sh DESCRIPTION
+This program compresses a kernel using
+.Xr gzip 1
+to reduce its disk storage requirements.
+It does not reduce the memory footprint once loaded into memory.
+You lose all the symbols, so usability is limited.
+Its main usage is making kernels for install and fixit floppies, etc.
+.Pp
+The following options are available:
+.Bl -tag -width flag
+.It Fl v
+Verbose mode, reports how much memory is being used by the kernel
+after compression. Also allows you to check to make sure your kernel
+is not going past the 4MB boundary.
+.It Fl l Ar loadaddr
+Specify the address to load the kernel into memory at.
+.Sh DIAGNOSTICS
+The
+.Nm
+utility returns with exit code 1 if given invalid arguments.
+Exit code two means
+.Nm
+was unable to read or process the kernel file.
+.Sh SEE ALSO
+.Xr gzip 1
+.\" .Sh STANDARDS
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.0.5 .
+Obtained from Linux via 386BSD -- based on tools/build.c by Linus Torvalds,
+and ported to 386BSD by Serge Vakulenko.
+.Sh AUTHORS
+This man page was written by David E. O'Brien.
+.\" .Sh BUGS
diff --git a/usr.bin/kzip/kzip.c b/usr.bin/kzip/kzip.c
new file mode 100644
index 0000000..f4b9782
--- /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.8 1997/03/29 04:30:19 imp 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 [-v] [ -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")) != -1) {
+ 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 & 0x00FFFFFF;
+
+ 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..9dea466
--- /dev/null
+++ b/usr.bin/last/last.c
@@ -0,0 +1,429 @@
+/*
+ * 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 <locale.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;
+
+ (void) setlocale(LC_TIME, "");
+
+ maxrec = -1;
+ while ((ch = getopt(argc, argv, "0123456789f:h:t:")) != -1)
+ 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, *ttx; /* ttylist entry */
+ struct stat stb; /* stat of file for size */
+ long bl, delta; /* time difference */
+ int bytes, wfd;
+ char *crmsg;
+ char ct[80];
+ struct tm *tm;
+
+ 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;) {
+ LIST_REMOVE(tt, list);
+ ttx = tt;
+ tt = tt->list.le_next;
+ free(ttx);
+ }
+ currentout = -bp->ut_time;
+ crmsg = strncmp(bp->ut_name, "shutdown",
+ UT_NAMESIZE) ? "crash" : "shutdown";
+ if (want(bp)) {
+ tm = localtime(&bp->ut_time);
+ (void) strftime(ct, sizeof(ct), "%c", tm);
+ 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)) {
+ tm = localtime(&bp->ut_time);
+ (void) strftime(ct, sizeof(ct), "%c", tm);
+ 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';
+ tm = localtime(&bp->ut_time);
+ (void) strftime(ct, sizeof(ct), "%c", tm);
+ 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 {
+ tm = localtime(&tt->logout);
+ (void) strftime(ct, sizeof(ct), "%c", tm);
+ printf("- %5.5s", ct + 11);
+ }
+ delta = tt->logout - bp->ut_time;
+ tm = gmtime(&delta);
+ (void) strftime(ct, sizeof(ct), "%c", tm);
+ if (delta < 86400)
+ printf(" (%5.5s)\n", ct + 11);
+ else
+ printf(" (%ld+%5.5s)\n",
+ delta / 86400, ct + 11);
+ }
+ LIST_REMOVE(tt, list);
+ free(tt);
+ if (maxrec != -1 && !--maxrec)
+ return;
+ } else {
+ tt->logout = bp->ut_time;
+ }
+ }
+ }
+ }
+ tm = localtime(&buf[0].ut_time);
+ (void) strftime(ct, sizeof(ct), "%c", tm);
+ 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[80];
+ struct tm *tm;
+
+ tm = localtime(&buf[0].ut_time);
+ (void) strftime(ct, sizeof(ct), "%c", tm);
+ 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..05455cf
--- /dev/null
+++ b/usr.bin/lastcomm/lastcomm.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.
+.\"
+.\" From: @(#)lastcomm.1 8.1 (Berkeley) 6/6/93
+.\" $Id$
+.\"
+.Dd September 18, 1996
+.Dt LASTCOMM 1
+.Os BSD 3
+.Sh NAME
+.Nm lastcomm
+.Nd show last commands executed in reverse order
+.Sh SYNOPSIS
+.Nm lastcomm
+.Op Fl EScesu
+.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
+The following options are available:
+.Pp
+.Bl -tag -width XXfXfileX -compact
+.Pp
+.It Fl E
+Print the time the process exited.
+.It Fl S
+Print the time the process started.
+.It Fl c
+Print the amount of cpu time used by the process.
+.It Fl e
+Print the amount of elapsed time used by the process.
+.It Fl s
+Print the amount of system time used by the process.
+.It Fl u
+Print the amount of user time used by the process.
+.It Fl f Ar file
+Read from
+.Ar file
+rather than the default
+.Pa /var/account/acct .
+.El
+.Pp
+If no options are specified,
+.Fl cS
+is assumed.
+If
+.Nm
+is invoked with arguments, only accounting entries with a
+matching
+.Ar command
+name,
+.Ar user
+name,
+or
+.Ar terminal
+name
+are printed.
+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
+.Pq Fl c ,
+wall
+.Pq Fl e ,
+system
+.Pq Fl s ,
+or user
+.Pq Fl u
+time used by the process (in seconds).
+.It
+The time the process started
+.Pq Fl S
+or exited
+.Pq Fl E .
+.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..6d7b034
--- /dev/null
+++ b/usr.bin/lastcomm/lastcomm.c
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ *
+ * $Id: lastcomm.c,v 1.7 1997/03/29 04:30:24 imp Exp $
+ */
+
+#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();
+
+#define AC_UTIME 1 /* user */
+#define AC_STIME 2 /* system */
+#define AC_ETIME 4 /* elapsed */
+#define AC_CTIME 8 /* user + system time, default */
+
+#define AC_BTIME 16 /* starting time */
+#define AC_FTIME 32 /* exit time (starting time + elapsed time )*/
+
+#define AC_HZ ((double)AHZ)
+
+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;
+ int time = 0;
+
+ acctfile = _PATH_ACCT;
+ while ((ch = getopt(argc, argv, "f:usecSE")) != -1)
+ switch((char)ch) {
+ case 'f':
+ acctfile = optarg;
+ break;
+
+ case 'u':
+ time |= AC_UTIME; /* user time */
+ break;
+ case 's':
+ time |= AC_STIME; /* system time */
+ break;
+ case 'e':
+ time |= AC_ETIME; /* elapsed time */
+ break;
+ case 'c':
+ time |= AC_CTIME; /* user + system time */
+ break;
+
+ case 'S':
+ time |= AC_BTIME; /* starting time */
+ break;
+ case 'E':
+ /* exit time (starting time + elapsed time )*/
+ time |= AC_FTIME;
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+
+ /* default user + system time and starting time */
+ if (!time) {
+ time = AC_CTIME | AC_BTIME;
+ }
+
+ 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;
+
+ (void)printf("%-*.*s %-7s %-*s %-*s ",
+ 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));
+
+
+ /* user + system time */
+ if (time & AC_CTIME) {
+ (void)printf("%6.2f secs ",
+ (expand(ab.ac_utime) +
+ expand(ab.ac_stime))/AC_HZ);
+ }
+
+ /* usr time */
+ if (time & AC_UTIME) {
+ (void)printf("%6.2f us ", expand(ab.ac_utime)/AC_HZ);
+ }
+
+ /* system time */
+ if (time & AC_STIME) {
+ (void)printf("%6.2f sy ", expand(ab.ac_stime)/AC_HZ);
+ }
+
+ /* elapsed time */
+ if (time & AC_ETIME) {
+ (void)printf("%8.2f es ", expand(ab.ac_etime)/AC_HZ);
+ }
+
+ /* starting time */
+ if (time & AC_BTIME) {
+ (void)printf("%.16s ", ctime(&ab.ac_btime));
+ }
+
+ /* exit time (starting time + elapsed time )*/
+ if (time & AC_FTIME) {
+ t = ab.ac_btime;
+ t += (time_t)(expand(ab.ac_etime)/AC_HZ);
+ (void)printf("%.16s ",
+ ctime(&t));
+ }
+ printf("\n");
+ }
+ 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 [-EScesu] [ -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..61c95a8
--- /dev/null
+++ b/usr.bin/ldd/Makefile
@@ -0,0 +1,7 @@
+# $Id$
+
+PROG= ldd
+SRCS= ldd.c sods.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..4b499fe
--- /dev/null
+++ b/usr.bin/ldd/ldd.1
@@ -0,0 +1,47 @@
+.Dd October 22, 1993
+.Dt LDD 1
+.Os FreeBSD
+.Sh NAME
+.Nm ldd
+.Nd list dynamic object dependencies
+.Sh SYNOPSIS
+.Nm ldd
+.Op Fl v
+.Op Fl f Ar format
+.Ar program ...
+.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.
+.Pp
+Zero, one or two
+.Fl f
+options may be given. The argument is a format string passed to
+.Xr rtld 1
+and allows customization of
+.Nm ldd Ns 's
+output. See
+.Xr rtld 1
+for a list of recognised conversion characters.
+.Pp
+The
+.Fl v
+option displays an verbose listing of the dynamic linking headers
+encoded in the executable. See the source code and include
+files for the definitive meaning of all the fields.
+.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.
+.Pp
+The
+.Fl v
+support is based on code written by John Polstra <jdp@polstra.com>
diff --git a/usr.bin/ldd/ldd.c b/usr.bin/ldd/ldd.c
new file mode 100644
index 0000000..331d3c7
--- /dev/null
+++ b/usr.bin/ldd/ldd.c
@@ -0,0 +1,170 @@
+/*
+ * 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$
+ */
+
+#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>
+
+extern void dump_filename __P((const char *));
+extern int error_count;
+
+void
+usage()
+{
+ fprintf(stderr, "usage: ldd [-v] [-f format] program ...\n");
+ exit(1);
+}
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ char *fmt1 = NULL, *fmt2 = NULL;
+ int rval;
+ int c;
+ int vflag = 0;
+
+ while ((c = getopt(argc, argv, "vf:")) != EOF) {
+ switch (c) {
+ case 'v':
+ vflag++;
+ break;
+ case 'f':
+ if (fmt1) {
+ if (fmt2)
+ errx(1, "Too many formats");
+ fmt2 = optarg;
+ } else
+ fmt1 = optarg;
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (vflag && fmt1)
+ errx(1, "-v may not be used with -f");
+
+ if (argc <= 0) {
+ usage();
+ /*NOTREACHED*/
+ }
+
+ if (vflag) {
+ for (c = 0; c < argc; c++)
+ dump_file(argv[c]);
+ exit(error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+ }
+
+ /* ld.so magic */
+ setenv("LD_TRACE_LOADED_OBJECTS", "1", 1);
+ if (fmt1)
+ setenv("LD_TRACE_LOADED_OBJECTS_FMT1", fmt1, 1);
+ if (fmt2)
+ setenv("LD_TRACE_LOADED_OBJECTS_FMT2", fmt2, 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);
+
+ setenv("LD_TRACE_LOADED_OBJECTS_PROGNAME", *argv, 1);
+ if (fmt1 == NULL && fmt2 == NULL)
+ /* Default formats */
+ 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/ldd/sods.c b/usr.bin/ldd/sods.c
new file mode 100644
index 0000000..b3732c1
--- /dev/null
+++ b/usr.bin/ldd/sods.c
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 1996 John D. Polstra. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 JOHN D. POLSTRA AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JOHN D. POLSTRA OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <a.out.h>
+#include <link.h>
+#include <stab.h>
+
+#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. */
+
+#ifndef N_SETV
+#define N_SETV 0x1C /* Pointer to set vector in data area. */
+#endif /* This is output from LD. */
+
+#ifdef STANDALONE
+static
+#endif
+void dump_file(const char *);
+
+static void dump_rels(const char *, const struct relocation_info *,
+ unsigned long, const char *(*)(unsigned long), unsigned char *);
+static void dump_segs();
+static void dump_sods();
+static void dump_sym(const struct nlist *);
+static void dump_syms();
+
+static void dump_rtrels();
+static void dump_rtsyms();
+
+static void error(const char *, ...);
+static const char *rtsym_name(unsigned long);
+static const char *sym_name(unsigned long);
+
+#ifdef STANDALONE
+static
+#endif
+int error_count;
+
+/*
+ * Variables ending in _base are pointers to things in our address space,
+ * i.e., in the file itself.
+ *
+ * Variables ending in _addr are adjusted according to where things would
+ * actually appear in memory if the file were loaded.
+ */
+static const char *file_base;
+static const char *text_base;
+static const char *data_base;
+static const struct relocation_info *rel_base;
+static const struct nlist *sym_base;
+static const char *str_base;
+
+static const struct relocation_info *rtrel_base;
+static const struct nzlist *rtsym_base;
+static const char *rtstr_base;
+
+static const struct exec *ex;
+static const struct _dynamic *dyn;
+static const struct section_dispatch_table *sdt;
+
+static const char *text_addr;
+static const char *data_addr;
+
+static unsigned long rel_count;
+static unsigned long sym_count;
+
+static unsigned long rtrel_count;
+static unsigned long rtsym_count;
+
+/* Dynamically allocated flags, 1 byte per symbol, to record whether each
+ symbol was referenced by a relocation entry. */
+static unsigned char *sym_used;
+static unsigned char *rtsym_used;
+
+static unsigned long origin; /* What values are relocated relative to */
+
+#ifdef STANDALONE
+main(int argc, char *argv[])
+{
+ int i;
+
+ for(i = 1; i < argc; ++i)
+ dump_file(argv[i]);
+
+ return error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+#endif
+
+#ifdef STANDALONE
+static
+#endif
+void
+dump_file(const char *fname)
+{
+ int fd;
+ struct stat sb;
+ caddr_t objbase;
+ long load_offset;
+
+ if(stat(fname, &sb) == -1) {
+ error("Cannot stat \"%s\"", fname);
+ return;
+ }
+
+ if((sb.st_mode & S_IFMT) != S_IFREG) {
+ error("\"%s\" is not a regular file", fname);
+ return;
+ }
+
+ if((fd = open(fname, O_RDONLY, 0)) == -1) {
+ error("Cannot open \"%s\"", fname);
+ return;
+ }
+
+ objbase = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if(objbase == (caddr_t) -1) {
+ error("Cannot mmap \"%s\"", fname);
+ close(fd);
+ return;
+ }
+
+ close(fd);
+
+ file_base = (const char *) objbase; /* Makes address arithmetic easier */
+
+ ex = (const struct exec *) file_base;
+
+ printf("%s: a_midmag = 0x%lx\n", fname, ex->a_midmag);
+ printf(" magic = 0x%x = 0%o, netmagic = 0x%x = 0%o\n",
+ N_GETMAGIC(*ex), N_GETMAGIC(*ex),
+ N_GETMAGIC_NET(*ex), N_GETMAGIC_NET(*ex));
+
+ if(N_BADMAG(*ex)) {
+ error("%s: Bad magic number", fname);
+ munmap(objbase, sb.st_size);
+ return;
+ }
+
+ printf(" a_text = 0x%lx\n", ex->a_text);
+ printf(" a_data = 0x%lx\n", ex->a_data);
+ printf(" a_bss = 0x%lx\n", ex->a_bss);
+ printf(" a_syms = 0x%lx\n", ex->a_syms);
+ printf(" a_entry = 0x%lx\n", ex->a_entry);
+ printf(" a_trsize = 0x%lx\n", ex->a_trsize);
+ printf(" a_drsize = 0x%lx\n", ex->a_drsize);
+
+ load_offset = N_TXTADDR(*ex) - N_TXTOFF(*ex);
+
+ text_base = file_base + N_TXTOFF(*ex);
+ data_base = file_base + N_DATOFF(*ex);
+ rel_base = (const struct relocation_info *) (file_base + N_RELOFF(*ex));
+ sym_base = (const struct nlist *) (file_base + N_SYMOFF(*ex));
+ str_base = file_base + N_STROFF(*ex);
+
+ rel_count = (ex->a_trsize + ex->a_drsize) / sizeof rel_base[0];
+ assert(rel_count * sizeof rel_base[0] == ex->a_trsize + ex->a_drsize);
+ sym_count = ex->a_syms / sizeof sym_base[0];
+ assert(sym_count * sizeof sym_base[0] == ex->a_syms);
+
+ if(sym_count != 0) {
+ sym_used = (unsigned char *) calloc(sym_count, sizeof(unsigned char));
+ assert(sym_used != NULL);
+ }
+
+ printf(" Entry = 0x%x, load offset = 0x%lx\n",
+ ex->a_entry, load_offset);
+ printf(" Text offset = %lx, address = %lx\n", N_TXTOFF(*ex),
+ N_TXTADDR(*ex));
+ printf(" Data offset = %lx, address = %lx\n", N_DATOFF(*ex),
+ N_DATADDR(*ex));
+
+ /*
+ * DEBUG
+ *
+ * In an executable program file, everything is relocated relative to
+ * the assumed run-time load address, i.e., N_TXTADDR(*ex), i.e., 0x1000.
+ *
+ * In a shared library file, everything is relocated relative to the
+ * start of the file, i.e., N_TXTOFF(*ex), i.e., 0.
+ *
+ * The way to tell the difference is by looking at ex->a_entry. If it
+ * is >= 0x1000, then we have an executable program. Otherwise, we
+ * have a shared library.
+ *
+ * When a program is executed, the entire file is mapped into memory,
+ * including the a.out header and so forth. But it is not mapped at
+ * address 0; rather it is mapped at address 0x1000. The first page
+ * of the user's address space is left unmapped in order to catch null
+ * pointer dereferences.
+ *
+ * In this program, when we map in an executable program, we have to
+ * simulate the empty page by decrementing our assumed base address by
+ * a pagesize.
+ */
+
+ text_addr = text_base;
+ data_addr = data_base;
+ origin = 0;
+
+ if(ex->a_entry >= load_offset) { /* Executable, not a shared library */
+ /*
+ * The fields in the object have already been relocated on the
+ * assumption that the object will be loaded at N_TXTADDR(*ex).
+ * We have to compensate for that.
+ */
+ text_addr -= load_offset;
+ data_addr -= load_offset;
+ origin = load_offset;
+ printf(" Program, origin = %lx\n", origin);
+ } else
+ printf(" Library, origin = %lx\n", origin);
+
+ if(N_GETFLAG(*ex) & EX_DYNAMIC) {
+ dyn = (const struct _dynamic *) data_base;
+ sdt = (const struct section_dispatch_table *)
+ (text_addr + (unsigned long) dyn->d_un.d_sdt);
+
+ rtrel_base =
+ (const struct relocation_info *) (text_addr + sdt->sdt_rel);
+ rtrel_count = (sdt->sdt_hash - sdt->sdt_rel) / sizeof rtrel_base[0];
+ assert(rtrel_count * sizeof rtrel_base[0] ==
+ sdt->sdt_hash - sdt->sdt_rel);
+
+ rtsym_base = (const struct nzlist *) (text_addr + sdt->sdt_nzlist);
+ rtsym_count = (sdt->sdt_strings - sdt->sdt_nzlist) /
+ sizeof rtsym_base[0];
+ assert(rtsym_count * sizeof rtsym_base[0] ==
+ sdt->sdt_strings - sdt->sdt_nzlist);
+
+ if(rtsym_count != 0) {
+ rtsym_used = (unsigned char *) calloc(rtsym_count,
+ sizeof(unsigned char));
+ assert(rtsym_used != NULL);
+ }
+
+ rtstr_base = text_addr + sdt->sdt_strings;
+ }
+
+ dump_segs();
+ dump_sods();
+ dump_rels("Relocations", rel_base, rel_count, sym_name, sym_used);
+ dump_syms();
+
+ dump_rels("Run-time relocations", rtrel_base, rtrel_count, rtsym_name,
+ rtsym_used);
+ dump_rtsyms();
+
+ if(rtsym_used != NULL) {
+ free(rtsym_used);
+ rtsym_used = NULL;
+ }
+ if(sym_used != NULL) {
+ free(sym_used);
+ sym_used = NULL;
+ }
+ munmap(objbase, sb.st_size);
+}
+
+static void
+dump_rels(const char *label, const struct relocation_info *base,
+ unsigned long count, const char *(*name)(unsigned long),
+ unsigned char *sym_used_flags)
+{
+ unsigned long i;
+
+ printf(" %s:\n", label);
+ for(i = 0; i < count; ++i) {
+ const struct relocation_info *r = &base[i];
+
+ printf(" %6lu %8x/%u %c%c%c%c%c%c", i,
+ r->r_address, 1u << r->r_length,
+ r->r_extern ? 'e' : '-',
+ r->r_jmptable ? 'j' : '-',
+ r->r_relative ? 'r' : '-',
+ r->r_baserel ? 'b' : '-',
+ r->r_pcrel ? 'p' : '-',
+ r->r_copy ? 'c' : '-');
+
+ if(r->r_extern || r->r_baserel || r->r_jmptable || r->r_copy) {
+ printf(" %4u %s", r->r_symbolnum, name(r->r_symbolnum));
+ sym_used_flags[r->r_symbolnum] = 1;
+ }
+
+ printf("\n");
+ }
+}
+
+static void
+dump_rtsyms()
+{
+ unsigned long i;
+
+ printf(" Run-time symbols:\n");
+ for(i = 0; i < rtsym_count; ++i) {
+ printf(" %6lu%c ", i, rtsym_used[i] ? '*' : ' ');
+ dump_sym(&rtsym_base[i].nlist);
+ printf("/%-5ld %s\n", rtsym_base[i].nz_size, rtsym_name(i));
+ }
+}
+
+static void
+dump_segs()
+{
+ printf(" Text segment starts at address %lx\n", origin + N_TXTOFF(*ex));
+ if(N_GETFLAG(*ex) & EX_DYNAMIC) {
+ printf(" rel starts at %lx\n", sdt->sdt_rel);
+ printf(" hash starts at %lx\n", sdt->sdt_hash);
+ printf(" nzlist starts at %lx\n", sdt->sdt_nzlist);
+ printf(" strings starts at %lx\n", sdt->sdt_strings);
+ }
+
+ printf(" Data segment starts at address %lx\n", origin + N_DATOFF(*ex));
+ if(N_GETFLAG(*ex) & EX_DYNAMIC) {
+ printf(" _dynamic starts at %lx\n", origin + N_DATOFF(*ex));
+ printf(" so_debug starts at %lx\n", (unsigned long) dyn->d_debug);
+ printf(" sdt starts at %lx\n", (unsigned long) dyn->d_un.d_sdt);
+ printf(" got starts at %lx\n", sdt->sdt_got);
+ printf(" plt starts at %lx\n", sdt->sdt_plt);
+ printf(" rest of stuff starts at %lx\n",
+ sdt->sdt_plt + sdt->sdt_plt_sz);
+ }
+}
+
+static void
+dump_sods()
+{
+ long sod_offset;
+ long paths_offset;
+
+ if(dyn == NULL) /* Not a shared object */
+ return;
+
+ sod_offset = sdt->sdt_sods;
+ printf(" Shared object dependencies:\n");
+ while(sod_offset != 0) {
+ const struct sod *sodp = (const struct sod *) (text_addr + sod_offset);
+ const char *name = (const char *) (text_addr + sodp->sod_name);
+
+ if (sodp->sod_library)
+ printf(" -l%-16s version %d.%d\n", name, sodp->sod_major,
+ sodp->sod_minor);
+ else
+ printf(" %s\n", name);
+ sod_offset = sodp->sod_next;
+ }
+ paths_offset = sdt->sdt_paths;
+ printf(" Shared object additional paths:\n");
+ if (paths_offset != 0) {
+ char *path = (char *)(text_addr + paths_offset);
+ printf(" %s\n", path);
+ } else {
+ printf(" NULL\n");
+ }
+}
+
+static void
+dump_sym(const struct nlist *np)
+{
+ char type[8];
+ char *p;
+
+ switch(np->n_type & ~N_EXT) {
+ case N_UNDF: strcpy(type, "undf"); break;
+ case N_ABS: strcpy(type, "abs"); break;
+ case N_TEXT: strcpy(type, "text"); break;
+ case N_DATA: strcpy(type, "data"); break;
+ case N_BSS: strcpy(type, "bss"); break;
+ case N_INDR: strcpy(type, "indr"); break;
+ case N_SIZE: strcpy(type, "size"); break;
+ case N_COMM: strcpy(type, "comm"); break;
+ case N_SETA: strcpy(type, "seta"); break;
+ case N_SETT: strcpy(type, "sett"); break;
+ case N_SETD: strcpy(type, "setd"); break;
+ case N_SETB: strcpy(type, "setb"); break;
+ case N_SETV: strcpy(type, "setv"); break;
+ case N_FN: strcpy(type, np->n_type&N_EXT ? "fn" : "warn"); break;
+ case N_GSYM: strcpy(type, "gsym"); break;
+ case N_FNAME: strcpy(type, "fname"); break;
+ case N_FUN: strcpy(type, "fun"); break;
+ case N_STSYM: strcpy(type, "stsym"); break;
+ case N_LCSYM: strcpy(type, "lcsym"); break;
+ case N_MAIN: strcpy(type, "main"); break;
+ case N_PC: strcpy(type, "pc"); break;
+ case N_RSYM: strcpy(type, "rsym"); break;
+ case N_SLINE: strcpy(type, "sline"); break;
+ case N_DSLINE: strcpy(type, "dsline"); break;
+ case N_BSLINE: strcpy(type, "bsline"); break;
+ case N_SSYM: strcpy(type, "ssym"); break;
+ case N_SO: strcpy(type, "so"); break;
+ case N_LSYM: strcpy(type, "lsym"); break;
+ case N_BINCL: strcpy(type, "bincl"); break;
+ case N_SOL: strcpy(type, "sol"); break;
+ case N_PSYM: strcpy(type, "psym"); break;
+ case N_EINCL: strcpy(type, "eincl"); break;
+ case N_ENTRY: strcpy(type, "entry"); break;
+ case N_LBRAC: strcpy(type, "lbrac"); break;
+ case N_EXCL: strcpy(type, "excl"); break;
+ case N_RBRAC: strcpy(type, "rbrac"); break;
+ case N_BCOMM: strcpy(type, "bcomm"); break;
+ case N_ECOMM: strcpy(type, "ecomm"); break;
+ case N_ECOML: strcpy(type, "ecoml"); break;
+ case N_LENG: strcpy(type, "leng"); break;
+ default: sprintf(type, "0x%02x", np->n_type);
+ }
+
+ if(np->n_type & N_EXT && type[0] != '0')
+ for(p = type; *p != '\0'; ++p)
+ *p = toupper(*p);
+
+ printf("%-5s %8lx", type, np->n_value);
+}
+
+static void
+dump_syms()
+{
+ unsigned long i;
+
+ printf(" Symbols:\n");
+ for(i = 0; i < sym_count; ++i) {
+ printf(" %6lu%c ", i, sym_used[i] ? '*' : ' ');
+ dump_sym(&sym_base[i]);
+ printf(" %s\n", sym_name(i));
+ }
+}
+
+static void
+error(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ putc('\n', stderr);
+
+ ++error_count;
+}
+
+static const char *
+rtsym_name(unsigned long n)
+{
+ assert(n < rtsym_count);
+ if(rtsym_base[n].nz_strx == 0)
+ return "";
+ return rtstr_base + rtsym_base[n].nz_strx;
+}
+
+static const char *
+sym_name(unsigned long n)
+{
+ assert(n < sym_count);
+ if(sym_base[n].n_un.n_strx == 0)
+ return "";
+ return str_base + sym_base[n].n_un.n_strx;
+}
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..cca4e5b
--- /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.3 (Berkeley) 4/28/95
+.\"
+.Dd April 28, 1995
+.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 \-s KILL
+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..6c9a950
--- /dev/null
+++ b/usr.bin/lex/FlexLexer.h
@@ -0,0 +1,185 @@
+// $Header: /home/daffy/u0/vern/flex/RCS/FlexLexer.h,v 1.19 96/05/25 20:43:02 vern Exp $
+
+// FlexLexer.h -- define interfaces for lexical analyzer classes 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.
+
+// This file defines FlexLexer, an abstract class which specifies the
+// external interface provided to flex C++ lexer objects, and yyFlexLexer,
+// which defines a particular lexer class.
+//
+// If you want to create multiple lexer classes, you use the -P flag
+// to rename each yyFlexLexer to some other xxFlexLexer. You then
+// include <FlexLexer.h> in your other sources once per lexer class:
+//
+// #undef yyFlexLexer
+// #define yyFlexLexer xxFlexLexer
+// #include <FlexLexer.h>
+//
+// #undef yyFlexLexer
+// #define yyFlexLexer zzFlexLexer
+// #include <FlexLexer.h>
+// ...
+
+#ifndef __FLEX_LEXER_H
+// Never included before - need to define base class.
+#define __FLEX_LEXER_H
+#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;
+
+ // Call yylex with new input/output sources.
+ int yylex( istream* new_in, ostream* new_out = 0 )
+ {
+ switch_streams( new_in, new_out );
+ return yylex();
+ }
+
+ // Switch to new input/output streams. A nil stream pointer
+ // indicates "keep the current one".
+ virtual void switch_streams( istream* new_in = 0,
+ ostream* new_out = 0 ) = 0;
+
+ int lineno() const { return yylineno; }
+
+ int debug() const { return yy_flex_debug; }
+ void set_debug( int flag ) { yy_flex_debug = flag; }
+
+protected:
+ char* yytext;
+ int yyleng;
+ int yylineno; // only maintained if you use %option yylineno
+ int yy_flex_debug; // only has effect with -d or "%option debug"
+};
+
+}
+#endif
+
+#if defined(yyFlexLexer) || ! defined(yyFlexLexerOnce)
+// Either this is the first time through (yyFlexLexerOnce not defined),
+// or this is a repeated include to define a different flavor of
+// yyFlexLexer, as discussed in the flex man page.
+#define yyFlexLexerOnce
+
+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 );
+
+ virtual ~yyFlexLexer();
+
+ 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();
+ virtual void switch_streams( istream* new_in, ostream* new_out );
+
+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 );
+ void yy_flush_buffer( struct yy_buffer_state* b );
+
+ 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;
+ int yy_more_offset;
+ int yy_prev_more_offset;
+};
+
+#endif
diff --git a/usr.bin/lex/Makefile b/usr.bin/lex/Makefile
new file mode 100644
index 0000000..63f36e9
--- /dev/null
+++ b/usr.bin/lex/Makefile
@@ -0,0 +1,59 @@
+# $Id$
+#
+# 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.
+#
+
+PROG= lex
+LINKS+= ${BINDIR}/lex ${BINDIR}/lex++
+LINKS+= ${BINDIR}/lex ${BINDIR}/flex
+LINKS+= ${BINDIR}/lex ${BINDIR}/flex++
+
+SRCS= scan.c ccl.c dfa.c ecs.c gen.c main.c misc.c nfa.c parse.c \
+ skel.c sym.c tblcmp.c yylex.c
+LFLAGS+= -is
+CFLAGS+= -I. -I${.CURDIR}
+MAN1= lex.1
+MLINKS+= lex.1 flex.1
+MLINKS+= lex.1 flex++.1
+MLINKS+= lex.1 lex++.1
+
+CLEANFILES+= parse.c parse.h scan.c y.tab.h y.tab.c
+
+.if !defined(NOLIB)
+SUBDIR= lib
+.endif
+
+beforeinstall:
+ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 644 \
+ ${.CURDIR}/FlexLexer.h ${DESTDIR}/usr/include/g++
+
+
+parse.c parse.h: parse.y
+ $(YACC) -d $(.CURDIR)/parse.y
+ mv -f y.tab.c parse.c
+ mv -f y.tab.h parse.h
+
+bootstrap: initscan.c
+ @cmp -s ${.CURDIR}/initscan.c scan.c || { \
+ echo "Bootstrapping flex" ; \
+ rm -f scan.c ; \
+ cp -f ${.CURDIR}/initscan.c scan.c ; \
+ }
+
+beforedepend: parse.h
+scan.o: parse.h
+
+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..3e23e7d
--- /dev/null
+++ b/usr.bin/lex/NEWS
@@ -0,0 +1,1233 @@
+Changes between release 2.5.4 (11Sep96) and release 2.5.3:
+
+ - Fixed a bug introduced in 2.5.3 that blew it when a call
+ to input() occurred at the end of an input file.
+
+ - Fixed scanner skeleton so the example in the man page of
+ scanning strings using exclusive start conditions works.
+
+ - Minor Makefile tweaks.
+
+
+Changes between release 2.5.3 (29May96) and release 2.5.2:
+
+ - Some serious bugs in yymore() have been fixed. In particular,
+ when using AT&T-lex-compatibility or %array, you can intermix
+ calls to input(), unput(), and yymore(). (This still doesn't
+ work for %pointer, and isn't likely to in the future.)
+
+ - A bug in handling NUL's in the input stream of scanners using
+ REJECT has been fixed.
+
+ - The default main() in libfl.a now repeatedly calls yylex() until
+ it returns 0, rather than just calling it once.
+
+ - Minor tweak for Windows NT Makefile, MISC/NT/Makefile.
+
+
+Changes between release 2.5.2 (25Apr95) and release 2.5.1:
+
+ - The --prefix configuration option now works.
+
+ - A bug that completely broke the "-Cf" table compression
+ option has been fixed.
+
+ - A major headache involving "const" declarators and Solaris
+ systems has been fixed.
+
+ - An octal escape sequence in a flex regular expression must
+ now contain only the digits 0-7.
+
+ - You can now use "--" on the flex command line to mark the
+ end of flex options.
+
+ - You can now specify the filename '-' as a synonym for stdin.
+
+ - By default, the scanners generated by flex no longer
+ statically initialize yyin and yyout to stdin and stdout.
+ This change is necessary because in some ANSI environments,
+ stdin and stdout are not compile-time constant. You can
+ force the initialization using "%option stdinit" in the first
+ section of your flex input.
+
+ - "%option nounput" now correctly omits the unput() routine
+ from the output.
+
+ - "make clean" now removes config.log, config.cache, and the
+ flex binary. The fact that it removes the flex binary means
+ you should take care if making changes to scan.l, to make
+ sure you don't wind up in a bootstrap problem.
+
+ - In general, the Makefile has been reworked somewhat (thanks
+ to Francois Pinard) for added flexibility - more changes will
+ follow in subsequent releases.
+
+ - The .texi and .info files in MISC/texinfo/ have been updated,
+ thanks also to Francois Pinard.
+
+ - The FlexLexer::yylex(istream* new_in, ostream* new_out) method
+ now does not have a default for the first argument, to disambiguate
+ it from FlexLexer::yylex().
+
+ - A bug in destructing a FlexLexer object before doing any scanning
+ with it has been fixed.
+
+ - A problem with including FlexLexer.h multiple times has been fixed.
+
+ - The alloca() chud necessary to accommodate bison has grown
+ even uglier, but hopefully more correct.
+
+ - A portability tweak has been added to accommodate compilers that
+ use char* generic pointers.
+
+ - EBCDIC contact information in the file MISC/EBCDIC has been updated.
+
+ - An OS/2 Makefile and config.h for flex 2.5 is now available in
+ MISC/OS2/, contributed by Kai Uwe Rommel.
+
+ - The descrip.mms file for building flex under VMS has been updated,
+ thanks to Pat Rankin.
+
+ - The notes on building flex for the Amiga have been updated for
+ flex 2.5, contributed by Andreas Scherer.
+
+
+Changes between release 2.5.1 (28Mar95) and release 2.4.7:
+
+ - A new concept of "start condition" scope has been introduced.
+ A start condition scope is begun with:
+
+ <SCs>{
+
+ where SCs is a list of one or more start conditions. Inside
+ the start condition scope, every rule automatically has the
+ prefix <SCs> applied to it, until a '}' which matches the
+ initial '{'. So, for example:
+
+ <ESC>{
+ "\\n" return '\n';
+ "\\r" return '\r';
+ "\\f" return '\f';
+ "\\0" return '\0';
+ }
+
+ is equivalent to:
+
+ <ESC>"\\n" return '\n';
+ <ESC>"\\r" return '\r';
+ <ESC>"\\f" return '\f';
+ <ESC>"\\0" return '\0';
+
+ As indicated in this example, rules inside start condition scopes
+ (and any rule, actually, other than the first) can be indented,
+ to better show the extent of the scope.
+
+ Start condition scopes may be nested.
+
+ - The new %option directive can be used in the first section of
+ a flex scanner to control scanner-generation options. Most
+ options are given simply as names, optionally preceded by the
+ word "no" (with no intervening whitespace) to negate their
+ meaning. Some are equivalent to flex flags, so putting them
+ in your scanner source is equivalent to always specifying
+ the flag (%option's take precedence over flags):
+
+ 7bit -7 option
+ 8bit -8 option
+ align -Ca option
+ backup -b option
+ batch -B option
+ c++ -+ option
+ caseful opposite of -i option (caseful is the default);
+ case-sensitive same as above
+ caseless -i option;
+ case-insensitive same as above
+ debug -d option
+ default opposite of -s option
+ ecs -Ce option
+ fast -F option
+ full -f option
+ interactive -I option
+ lex-compat -l option
+ meta-ecs -Cm option
+ perf-report -p option
+ read -Cr option
+ stdout -t option
+ verbose -v option
+ warn opposite of -w option (so use "%option nowarn" for -w)
+
+ array equivalent to "%array"
+ pointer equivalent to "%pointer" (default)
+
+ Some provide new features:
+
+ always-interactive generate a scanner which always
+ considers its input "interactive" (no call to isatty()
+ will be made when the scanner runs)
+ main supply a main program for the scanner, which
+ simply calls yylex(). Implies %option noyywrap.
+ never-interactive generate a scanner which never
+ considers its input "interactive" (no call to isatty()
+ will be made when the scanner runs)
+ stack if set, enable start condition stacks (see below)
+ stdinit if unset ("%option nostdinit"), initialize yyin
+ and yyout statically to nil FILE* pointers, instead
+ of stdin and stdout
+ yylineno if set, keep track of the current line
+ number in global yylineno (this option is expensive
+ in terms of performance). The line number is available
+ to C++ scanning objects via the new member function
+ lineno().
+ yywrap if unset ("%option noyywrap"), scanner does not
+ call yywrap() upon EOF but simply assumes there
+ are no more files to scan
+
+ Flex scans your rule actions to determine whether you use the
+ REJECT or yymore features (this is not new). Two %options can be
+ used to override its decision, either by setting them to indicate
+ the feature is indeed used, or unsetting them to indicate it
+ actually is not used:
+
+ reject
+ yymore
+
+ Three %option's take string-delimited values, offset with '=':
+
+ outfile="<name>" equivalent to -o<name>
+ prefix="<name>" equivalent to -P<name>
+ yyclass="<name>" set the name of the C++ scanning class
+ (see below)
+
+ A number of %option's are available for lint purists who
+ want to suppress the appearance of unneeded routines in
+ the generated scanner. Each of the following, if unset,
+ results in the corresponding routine not appearing in the
+ generated scanner:
+
+ input, unput
+ yy_push_state, yy_pop_state, yy_top_state
+ yy_scan_buffer, yy_scan_bytes, yy_scan_string
+
+ You can specify multiple options with a single %option directive,
+ and multiple directives in the first section of your flex input file.
+
+ - The new function:
+
+ YY_BUFFER_STATE yy_scan_string( const char *str )
+
+ returns a YY_BUFFER_STATE (which also becomes the current input
+ buffer) for scanning the given string, which occurs starting
+ with the next call to yylex(). The string must be NUL-terminated.
+ A related function:
+
+ YY_BUFFER_STATE yy_scan_bytes( const char *bytes, int len )
+
+ creates a buffer for scanning "len" bytes (including possibly NUL's)
+ starting at location "bytes".
+
+ Note that both of these functions create and scan a *copy* of
+ the string/bytes. (This may be desirable, since yylex() modifies
+ the contents of the buffer it is scanning.) You can avoid the
+ copy by using:
+
+ YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+
+ which scans in place the buffer starting at "base", consisting
+ of "size" bytes, the last two bytes of which *must* be
+ YY_END_OF_BUFFER_CHAR (these bytes are not scanned; thus, scanning
+ consists of base[0] through base[size-2], inclusive). If you
+ fail to set up "base" in this manner, yy_scan_buffer returns a
+ nil pointer instead of creating a new input buffer.
+
+ The type yy_size_t is an integral type to which you can cast
+ an integer expression reflecting the size of the buffer.
+
+ - Three new routines are available for manipulating stacks of
+ start conditions:
+
+ void yy_push_state( int new_state )
+
+ pushes the current start condition onto the top of the stack
+ and BEGIN's "new_state" (recall that start condition names are
+ also integers).
+
+ void yy_pop_state()
+
+ pops the top of the stack and BEGIN's to it, and
+
+ int yy_top_state()
+
+ returns the top of the stack without altering the stack's
+ contents.
+
+ The start condition stack grows dynamically and so has no built-in
+ size limitation. If memory is exhausted, program execution
+ is aborted.
+
+ To use start condition stacks, your scanner must include
+ a "%option stack" directive.
+
+ - flex now supports POSIX character class expressions. These
+ are expressions enclosed inside "[:" and ":]" delimiters (which
+ themselves must appear between the '[' and ']' of a character
+ class; other elements may occur inside the character class, too).
+ The expressions flex recognizes are:
+
+ [:alnum:] [:alpha:] [:blank:] [:cntrl:] [:digit:] [:graph:]
+ [:lower:] [:print:] [:punct:] [:space:] [:upper:] [:xdigit:]
+
+ These expressions all designate a set of characters equivalent to
+ the corresponding isXXX function (for example, [:alnum:] designates
+ those characters for which isalnum() returns true - i.e., any
+ alphabetic or numeric). Some systems don't provide isblank(),
+ so flex defines [:blank:] as a blank or a tab.
+
+ For example, the following character classes are all equivalent:
+
+ [[:alnum:]]
+ [[:alpha:][:digit:]
+ [[:alpha:]0-9]
+ [a-zA-Z0-9]
+
+ If your scanner is case-insensitive (-i flag), then [:upper:]
+ and [:lower:] are equivalent to [:alpha:].
+
+ - The promised rewrite of the C++ FlexLexer class has not yet
+ been done. Support for FlexLexer is limited at the moment to
+ fixing show-stopper bugs, so, for example, the new functions
+ yy_scan_string() & friends are not available to FlexLexer
+ objects.
+
+ - The new macro
+
+ yy_set_interactive(is_interactive)
+
+ can be used to control whether the current buffer is considered
+ "interactive". An interactive buffer is processed more slowly,
+ but must be used when the scanner's input source is indeed
+ interactive to avoid problems due to waiting to fill buffers
+ (see the discussion of the -I flag in flex.1). A non-zero value
+ in the macro invocation marks the buffer as interactive, a zero
+ value as non-interactive. Note that use of this macro overrides
+ "%option always-interactive" or "%option never-interactive".
+
+ yy_set_interactive() must be invoked prior to beginning to
+ scan the buffer.
+
+ - The new macro
+
+ yy_set_bol(at_bol)
+
+ can be used to control whether the current buffer's scanning
+ context for the next token match is done as though at the
+ beginning of a line (non-zero macro argument; makes '^' anchored
+ rules active) or not at the beginning of a line (zero argument,
+ '^' rules inactive).
+
+ - Related to this change, the mechanism for determining when a scan is
+ starting at the beginning of a line has changed. It used to be
+ that '^' was active iff the character prior to that at which the
+ scan started was a newline. The mechanism now is that '^' is
+ active iff the last token ended in a newline (or the last call to
+ input() returned a newline). For most users, the difference in
+ mechanisms is negligible. Where it will make a difference,
+ however, is if unput() or yyless() is used to alter the input
+ stream. When in doubt, use yy_set_bol().
+
+ - The new beginning-of-line mechanism involved changing some fairly
+ twisted code, so it may have introduced bugs - beware ...
+
+ - The macro YY_AT_BOL() returns true if the next token scanned from
+ the current buffer will have '^' rules active, false otherwise.
+
+ - The new function
+
+ void yy_flush_buffer( struct yy_buffer_state* b )
+
+ flushes the contents of the current buffer (i.e., next time
+ the scanner attempts to match a token using b as the current
+ buffer, it will begin by invoking YY_INPUT to fill the buffer).
+ This routine is also available to C++ scanners (unlike some
+ of the other new routines).
+
+ The related macro
+
+ YY_FLUSH_BUFFER
+
+ flushes the contents of the current buffer.
+
+ - A new "-ooutput" option writes the generated scanner to "output".
+ If used with -t, the scanner is still written to stdout, but
+ its internal #line directives (see previous item) use "output".
+
+ - Flex now generates #line directives relating the code it
+ produces to the output file; this means that error messages
+ in the flex-generated code should be correctly pinpointed.
+
+ - When generating #line directives, filenames with embedded '\'s
+ have those characters escaped (i.e., turned into '\\'). This
+ feature helps with reporting filenames for some MS-DOS and OS/2
+ systems.
+
+ - The FlexLexer class includes two new public member functions:
+
+ virtual void switch_streams( istream* new_in = 0,
+ ostream* new_out = 0 )
+
+ reassigns yyin to new_in (if non-nil) and yyout to new_out
+ (ditto), deleting the previous input buffer if yyin is
+ reassigned. It is used by:
+
+ int yylex( istream* new_in = 0, ostream* new_out = 0 )
+
+ which first calls switch_streams() and then returns the value
+ of calling yylex().
+
+ - C++ scanners now have yy_flex_debug as a member variable of
+ FlexLexer rather than a global, and member functions for testing
+ and setting it.
+
+ - When generating a C++ scanning class, you can now use
+
+ %option yyclass="foo"
+
+ to inform flex that you have derived "foo" as a subclass of
+ yyFlexLexer, so flex will place your actions in the member
+ function foo::yylex() instead of yyFlexLexer::yylex(). It also
+ generates a yyFlexLexer::yylex() member function that generates a
+ run-time error if called (by invoking yyFlexLexer::LexerError()).
+ This feature is necessary if your subclass "foo" introduces some
+ additional member functions or variables that you need to access
+ from yylex().
+
+ - Current texinfo files in MISC/texinfo, contributed by Francois
+ Pinard.
+
+ - You can now change the name "flex" to something else (e.g., "lex")
+ by redefining $(FLEX) in the Makefile.
+
+ - Two bugs (one serious) that could cause "bigcheck" to fail have
+ been fixed.
+
+ - A number of portability/configuration changes have been made
+ for easier portability.
+
+ - You can use "YYSTATE" in your scanner as an alias for YY_START
+ (for AT&T lex compatibility).
+
+ - input() now maintains yylineno.
+
+ - input() no longer trashes yytext.
+
+ - interactive scanners now read characters in YY_INPUT up to a
+ newline, a large performance gain.
+
+ - C++ scanner objects now work with the -P option. You include
+ <FlexLexer.h> once per scanner - see comments in <FlexLexer.h>
+ (or flex.1) for details.
+
+ - C++ FlexLexer objects now use the "cerr" stream to report -d output
+ instead of stdio.
+
+ - The -c flag now has its full glorious POSIX interpretation (do
+ nothing), rather than being interpreted as an old-style -C flag.
+
+ - Scanners generated by flex now include two #define's giving
+ the major and minor version numbers (YY_FLEX_MAJOR_VERSION,
+ YY_FLEX_MINOR_VERSION). These can then be tested to see
+ whether certain flex features are available.
+
+ - Scanners generated using -l lex compatibility now have the symbol
+ YY_FLEX_LEX_COMPAT #define'd.
+
+ - When initializing (i.e., yy_init is non-zero on entry to yylex()),
+ generated scanners now set yy_init to zero before executing
+ YY_USER_INIT. This means that you can set yy_init back to a
+ non-zero value in YY_USER_INIT if you need the scanner to be
+ reinitialized on the next call.
+
+ - You can now use "#line" directives in the first section of your
+ scanner specification.
+
+ - When generating full-table scanners (-Cf), flex now puts braces
+ around each row of the 2-d array initialization, to silence warnings
+ on over-zealous compilers.
+
+ - Improved support for MS-DOS. The flex sources have been successfully
+ built, unmodified, for Borland 4.02 (all that's required is a
+ Borland Makefile and config.h file, which are supplied in
+ MISC/Borland - contributed by Terrence O Kane).
+
+ - Improved support for Macintosh using Think C - the sources should
+ build for this platform "out of the box". Contributed by Scott
+ Hofmann.
+
+ - Improved support for VMS, in MISC/VMS/, contributed by Pat Rankin.
+
+ - Support for the Amiga, in MISC/Amiga/, contributed by Andreas
+ Scherer. Note that the contributed files were developed for
+ flex 2.4 and have not been tested with flex 2.5.
+
+ - Some notes on support for the NeXT, in MISC/NeXT, contributed
+ by Raf Schietekat.
+
+ - The MISC/ directory now includes a preformatted version of flex.1
+ in flex.man, and pre-yacc'd versions of parse.y in parse.{c,h}.
+
+ - The flex.1 and flexdoc.1 manual pages have been merged. There
+ is now just one document, flex.1, which includes an overview
+ at the beginning to help you find the section you need.
+
+ - Documentation now clarifies that start conditions persist across
+ switches to new input files or different input buffers. If you
+ want to e.g., return to INITIAL, you must explicitly do so.
+
+ - The "Performance Considerations" section of the manual has been
+ updated.
+
+ - Documented the "yy_act" variable, which when YY_USER_ACTION is
+ invoked holds the number of the matched rule, and added an
+ example of using yy_act to profile how often each rule is matched.
+
+ - Added YY_NUM_RULES, a definition that gives the total number
+ of rules in the file, including the default rule (even if you
+ use -s).
+
+ - Documentation now clarifies that you can pass a nil FILE* pointer
+ to yy_create_buffer() or yyrestart() if you've arrange YY_INPUT
+ to not need yyin.
+
+ - Documentation now clarifies that YY_BUFFER_STATE is a pointer to
+ an opaque "struct yy_buffer_state".
+
+ - Documentation now stresses that you gain the benefits of removing
+ backing-up states only if you remove *all* of them.
+
+ - Documentation now points out that traditional lex allows you
+ to put the action on a separate line from the rule pattern if
+ the pattern has trailing whitespace (ugh!), but flex doesn't
+ support this.
+
+ - A broken example in documentation of the difference between
+ inclusive and exclusive start conditions is now fixed.
+
+ - Usage (-h) report now goes to stdout.
+
+ - Version (-V) info now goes to stdout.
+
+ - More #ifdef chud has been added to the parser in attempt to
+ deal with bison's use of alloca().
+
+ - "make clean" no longer deletes emacs backup files (*~).
+
+ - Some memory leaks have been fixed.
+
+ - A bug was fixed in which dynamically-expanded buffers were
+ reallocated a couple of bytes too small.
+
+ - A bug was fixed which could cause flex to read and write beyond
+ the end of the input buffer.
+
+ - -S will not be going away.
+
+
+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.
+
+ - 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..7a4224d
--- /dev/null
+++ b/usr.bin/lex/README
@@ -0,0 +1,60 @@
+This is release 2.5 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.5 beta-testers for finding bugs and helping test and
+increase portability: Stan Adermann, Scott David Daniels, Charles Elliott,
+Joe Gayda, Chris Meier, James Nordby, Terrence O'Kane, Karsten Pahnke,
+Francois Pinard, Pat Rankin, Andreas Scherer, Marc Wiese, Nathan Zelle.
+
+Please send bug reports and feedback to: Vern Paxson (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
+
+ conf.in, 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
+ skel.c pre-converted C version of flex.skl
+
+ 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
+
+ flex.1 user 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..fe28463
--- /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/daffy/u0/vern/flex/RCS/ccl.c,v 2.9 93/09/16 20:32:14 vern 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/config.h b/usr.bin/lex/config.h
new file mode 100644
index 0000000..dc4481c
--- /dev/null
+++ b/usr.bin/lex/config.h
@@ -0,0 +1,26 @@
+/* config.h. Generated automatically by configure. */
+/* $Header: /home/daffy/u0/vern/flex/RCS/conf.in,v 1.2 95/01/09 12:11:51 vern Exp $ */
+
+/* Define to empty if the keyword does not work. */
+/* #undef const */
+
+/* Define to `unsigned' if <sys/types.h> doesn't define. */
+/* #undef size_t */
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if you have the <malloc.h> header file. */
+/* #undef HAVE_MALLOC_H */
+
+/* Define if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix). */
+/* #undef HAVE_ALLOCA_H */
+
+/* Define if platform-specific command line handling is necessary. */
+/* #undef NEED_ARGV_FIXUP */
diff --git a/usr.bin/lex/dfa.c b/usr.bin/lex/dfa.c
new file mode 100644
index 0000000..3647c2c
--- /dev/null
+++ b/usr.bin/lex/dfa.c
@@ -0,0 +1,1095 @@
+/* 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.2 1996/06/19 20:26:04 nate 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;
+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 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[0].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.
+ */
+ out_str_dec( "static yyconst %s yy_nxt[][%d] =\n {\n",
+ /* '}' so vi doesn't get too confused */
+ long_align ? "long" : "short", num_full_table_rows );
+
+ outn( " {" );
+
+ /* Generate 0 entries for state #0. */
+ for ( i = 0; i < num_full_table_rows; ++i )
+ mk2data( 0 );
+
+ dataflush();
+ outn( " },\n" );
+ }
+
+ /* 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;
+ }
+ }
+
+ if ( caseins && ! useecs )
+ {
+ register int j;
+
+ for ( i = 'A', j = 'a'; i <= 'Z'; ++i, ++j )
+ {
+ if ( state[i] == 0 && state[j] != 0 )
+ /* We're adding a transition. */
+ ++totaltrans;
+
+ else if ( state[i] != 0 && state[j] == 0 )
+ /* We're taking away a transition. */
+ --totaltrans;
+
+ state[i] = state[j];
+ }
+ }
+
+ numsnpairs += totaltrans;
+
+ 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 )
+ {
+ outn( " {" );
+
+ /* 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 );
+
+ dataflush();
+ outn( " },\n" );
+ }
+
+ 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..10b167c
--- /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/daffy/u0/vern/flex/RCS/ecs.c,v 2.9 93/12/07 10:18:20 vern 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.skl b/usr.bin/lex/flex.skl
new file mode 100644
index 0000000..8dee386
--- /dev/null
+++ b/usr.bin/lex/flex.skl
@@ -0,0 +1,1541 @@
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header: /home/ncvs/src/usr.bin/lex/flex.skl,v 1.1.1.2 1996/06/19 20:26:06 nate Exp $
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+%-
+#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 */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#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. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* 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". */
+#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;
+%*
+
+#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_RESTORE_YY_MORE_OFFSET \
+ 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 )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+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.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* 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 we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ /* 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;
+
+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 ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+%*
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+%% yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here
+
+%- 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(( yyconst 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.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+%-
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+%*
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YY_PROTO(( yyconst char * ));
+#endif
+
+#ifndef YY_NO_INPUT
+%- Standard (non-C++) definition
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+%*
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#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_RULE_SETUP definition goes here
+
+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 )
+ {
+ yy_init = 0;
+
+#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_current_buffer =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_load_buffer_state();
+ }
+
+ 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
+
+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 = (int) (yy_cp - yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ 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 */
+
+%+
+yyFlexLexer::yyFlexLexer( istream* arg_yyin, ostream* arg_yyout )
+ {
+ yyin = arg_yyin;
+ yyout = arg_yyout;
+ yy_c_buf_p = 0;
+ yy_init = 1;
+ yy_start = 0;
+ yy_flex_debug = 0;
+ yylineno = 1; // this will only get updated if %option yylineno
+
+ yy_did_buffer_switch_on_eof = 0;
+
+ yy_looking_for_trail_begin = 0;
+ yy_more_flag = 0;
+ yy_more_len = 0;
+ yy_more_offset = yy_prev_more_offset = 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
+ }
+
+yyFlexLexer::~yyFlexLexer()
+ {
+ delete yy_state_buf;
+ yy_delete_buffer( yy_current_buffer );
+ }
+
+void yyFlexLexer::switch_streams( istream* new_in, ostream* new_out )
+ {
+ if ( new_in )
+ {
+ yy_delete_buffer( yy_current_buffer );
+ yy_switch_to_buffer( yy_create_buffer( new_in, YY_BUF_SIZE ) );
+ }
+
+ if ( new_out )
+ yyout = new_out;
+ }
+
+#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;
+ 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 single character, 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 = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+ 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_current_buffer->yy_n_chars = 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 =
+ (int) (yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yy_flex_realloc( (void *) b->yy_ch_buf,
+ b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ 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 );
+
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ if ( yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ 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_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+ 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;
+ }
+
+
+%-
+#ifndef YY_NO_UNPUT
+#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 += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ yy_current_buffer->yy_n_chars =
+ 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" );
+ }
+
+ *--yy_cp = (char) c;
+
+%% update yylineno here
+
+ yytext_ptr = yy_bp;
+ yy_hold_char = *yy_cp;
+ yy_c_buf_p = yy_cp;
+ }
+%-
+#endif /* ifndef YY_NO_UNPUT */
+%*
+
+
+%-
+#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 */
+ int offset = yy_c_buf_p - yytext_ptr;
+ ++yy_c_buf_p;
+
+ switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart( yyin );
+
+ /* fall through */
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap() )
+ return EOF;
+
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p = yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ 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;
+
+%% update BOL and yylineno
+
+ 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()" );
+
+ b->yy_is_our_buffer = 1;
+
+ 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 )
+ return;
+
+ if ( b == yy_current_buffer )
+ yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yy_flex_free( (void *) b->yy_ch_buf );
+
+ yy_flex_free( (void *) b );
+ }
+
+
+%-
+#ifndef YY_ALWAYS_INTERACTIVE
+#ifndef YY_NEVER_INTERACTIVE
+extern int isatty YY_PROTO(( int ));
+#endif
+#endif
+
+#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
+
+%+
+extern "C" int isatty YY_PROTO(( int ));
+void yyFlexLexer::yy_init_buffer( YY_BUFFER_STATE b, istream* file )
+%*
+
+ {
+ yy_flush_buffer( b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+%-
+#if YY_ALWAYS_INTERACTIVE
+ b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+ b->yy_is_interactive = 0;
+#else
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+%+
+ b->yy_is_interactive = 0;
+%*
+ }
+
+
+%-
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+%+
+void yyFlexLexer::yy_flush_buffer( YY_BUFFER_STATE b )
+%*
+ {
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* 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[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == yy_current_buffer )
+ yy_load_buffer_state();
+ }
+%*
+
+
+#ifndef YY_NO_SCAN_BUFFER
+%-
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+ {
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer( b );
+
+ return b;
+ }
+%*
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+%-
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str )
+#else
+YY_BUFFER_STATE yy_scan_string( yy_str )
+yyconst char *yy_str;
+#endif
+ {
+ int len;
+ for ( len = 0; yy_str[len]; ++len )
+ ;
+
+ return yy_scan_bytes( yy_str, len );
+ }
+%*
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+%-
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+ {
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = len + 2;
+ buf = (char *) yy_flex_alloc( n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < len; ++i )
+ buf[i] = bytes[i];
+
+ buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+ }
+%*
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+%-
+#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 )
+ {
+ yy_size_t 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);
+ }
+#endif
+
+
+#ifndef YY_NO_POP_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]);
+ }
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+%-
+static int yy_top_state()
+%+
+int yyFlexLexer::yy_top_state()
+%*
+ {
+ return yy_start_stack[yy_start_stack_ptr - 1];
+ }
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+%-
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+ {
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+ }
+
+%+
+
+void yyFlexLexer::LexerError( yyconst char msg[] )
+ {
+ cerr << msg << '\n';
+ exit( YY_EXIT_FAILURE );
+ }
+%*
+
+
+/* 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_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, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+ {
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+ }
+#endif
+
+#ifdef YY_NEED_STRLEN
+#ifdef YY_USE_PROTOS
+static int yy_flex_strlen( yyconst char *s )
+#else
+static int yy_flex_strlen( s )
+yyconst char *s;
+#endif
+ {
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+ }
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+ {
+ return (void *) malloc( size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+ {
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) 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 );
+ }
+
+#if YY_MAIN
+int main()
+ {
+ yylex();
+ return 0;
+ }
+#endif
diff --git a/usr.bin/lex/flexdef.h b/usr.bin/lex/flexdef.h
new file mode 100644
index 0000000..3969fdf
--- /dev/null
+++ b/usr.bin/lex/flexdef.h
@@ -0,0 +1,1048 @@
+/* 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.2 1996/06/19 20:26:08 nate Exp $ (LBL) */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "config.h"
+
+#ifdef __TURBOC__
+#define HAVE_STRING_H 1
+#define MS_DOS 1
+#ifndef __STDC__
+#define __STDC__ 1
+#endif
+ #pragma warn -pro
+ #pragma warn -rch
+ #pragma warn -use
+ #pragma warn -aus
+ #pragma warn -par
+ #pragma warn -pia
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#endif
+
+/* As an aid for the internationalization patch to flex, which
+ * is maintained outside this distribution for copyright reasons.
+ */
+#define _(String) (String)
+
+/* 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
+#if __STDC__
+#define PROTO(proto) proto
+#else
+#define PROTO(proto) ()
+#endif
+#endif
+
+#ifdef VMS
+#ifndef __VMS_POSIX
+#define unlink remove
+#define SHORT_FILE_NAMES
+#endif
+#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
+#define unspecified -1
+
+
+/* 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 14
+
+/* 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 */
+
+/* Maximum number of NFA states. */
+#define MAXIMUM_MNS 31999
+
+/* Enough so that if it's subtracted from an NFA state number, the result
+ * is guaranteed to be negative.
+ */
+#define MARKER_DIFFERENCE (MAXIMUM_MNS+2)
+
+/* 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
+ * do_yylineno - if true, generate code to maintain yylineno
+ * 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.
+ * do_yywrap - do yywrap() processing on EOF. If false, EOF treated as
+ * "no more files".
+ * 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)
+ * in_rule - true if we're inside an individual rule, false if not.
+ * yymore_really_used - whether to treat yymore() as really used, regardless
+ * of what we think based on references to it in the user's actions.
+ * reject_really_used - same for REJECT
+ */
+
+extern int printstats, syntaxerror, eofseen, ddebug, trace, nowarn, spprdflt;
+extern int interactive, caseins, lex_compat, do_yylineno;
+extern int useecs, fulltbl, usemecs, fullspd;
+extern int gen_line_dirs, performance_report, backing_up_report;
+extern int C_plus_plus, long_align, use_read, yytext_is_array, do_yywrap;
+extern int csize;
+extern int yymore_used, reject, real_reject, continued_action, in_rule;
+
+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
+ * out_linenum - current output 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
+ * outfilename - name of output file
+ * did_outfilename - whether outfilename was explicitly set
+ * prefix - the prefix used for externally visible names ("yy" by default)
+ * yyclass - yyFlexLexer subclass to use for YY_DECL
+ * do_stdinit - whether to initialize yyin/yyout to stdin/stdout
+ * use_stdout - the -t flag
+ * 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, out_linenum;
+extern FILE *skelfile, *yyin, *backing_up_file;
+extern const char *skel[];
+extern int skel_ind;
+extern char *infilename, *outfilename;
+extern int did_outfilename;
+extern char *prefix, *yyclass;
+extern int do_stdinit, use_stdout;
+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, current_max_rules;
+extern int num_rules, num_eof_rules, default_rule, 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
+ */
+
+extern int lastsc, *scset, *scbol, *scxclu, *sceof;
+extern int current_max_scs;
+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, *cclmap, *ccllen, *cclng, cclreuse;
+extern int current_maxccls, 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, size_t));
+void *reallocate_array PROTO((void*, int, size_t));
+
+void *flex_alloc PROTO((size_t));
+void *flex_realloc PROTO((void*, size_t));
+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 */
+
+/* Check a DFA state for backing up. */
+extern void check_for_backing_up PROTO((int, int[]));
+
+/* Check to see if NFA state set constitutes "dangerous" trailing context. */
+extern void check_trailing_context PROTO((int*, int, int*, int));
+
+/* Construct the epsilon closure of a set of ndfa states. */
+extern int *epsclosure PROTO((int*, int*, int[], int*, int*));
+
+/* Increase the maximum number of dfas. */
+extern void increase_max_dfas PROTO((void));
+
+extern void ntod PROTO((void)); /* convert a ndfa to a dfa */
+
+/* Converts a set of ndfa states into a dfa state. */
+extern int snstods PROTO((int[], int, int[], int, int, int*));
+
+
+/* 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 do_indent PROTO((void)); /* indent to the current level */
+
+/* Generate the code to keep backing-up information. */
+extern void gen_backing_up PROTO((void));
+
+/* Generate the code to perform the backing up. */
+extern void gen_bu_action PROTO((void));
+
+/* Generate full speed compressed transition table. */
+extern void genctbl PROTO((void));
+
+/* Generate the code to find the action number. */
+extern void gen_find_action PROTO((void));
+
+extern void genftbl PROTO((void)); /* generate full transition table */
+
+/* Generate the code to find the next compressed-table state. */
+extern void gen_next_compressed_state PROTO((char*));
+
+/* Generate the code to find the next match. */
+extern void gen_next_match PROTO((void));
+
+/* Generate the code to find the next state. */
+extern void gen_next_state PROTO((int));
+
+/* Generate the code to make a NUL transition. */
+extern void gen_NUL_trans PROTO((void));
+
+/* Generate the code to find the start state. */
+extern void gen_start_state PROTO((void));
+
+/* Generate data statements for the transition tables. */
+extern void gentabs PROTO((void));
+
+/* Write out a formatted string at the current indentation level. */
+extern void indent_put2s PROTO((char[], char[]));
+
+/* Write out a string + newline at the current indentation level. */
+extern void indent_puts PROTO((char[]));
+
+extern void make_tables PROTO((void)); /* generate transition tables */
+
+
+/* from file main.c */
+
+extern void check_options PROTO((void));
+extern void flexend PROTO((int));
+extern void usage PROTO((void));
+
+
+/* from file misc.c */
+
+/* Add a #define to the action file. */
+extern void action_define PROTO(( char *defname, int value ));
+
+/* 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));
+
+/* Replace upper-case letter to lower-case. */
+extern Char clower PROTO((int));
+
+/* Returns a dynamically allocated copy of a string. */
+extern char *copy_string PROTO((register const char *));
+
+/* Returns a dynamically allocated copy of a (potentially) unsigned string. */
+extern Char *copy_unsigned_string PROTO((register Char *));
+
+/* Shell sort a character array. */
+extern void cshell PROTO((Char [], int, int));
+
+/* Finish up a block of data declarations. */
+extern void dataend PROTO((void));
+
+/* Flush generated data statements. */
+extern void dataflush PROTO((void));
+
+/* Report an error message and terminate. */
+extern void flexerror PROTO((const char[]));
+
+/* Report a fatal error message and terminate. */
+extern void flexfatal PROTO((const char[]));
+
+/* Convert a hexadecimal digit string to an integer value. */
+extern int htoi PROTO((Char[]));
+
+/* Report an error message formatted with one integer argument. */
+extern void lerrif PROTO((const char[], int));
+
+/* Report an error message formatted with one string argument. */
+extern void lerrsf PROTO((const char[], const char[]));
+
+/* Spit out a "#line" statement. */
+extern void line_directive_out PROTO((FILE*, int));
+
+/* 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 character corresponding to escape sequence. */
+extern Char myesc PROTO((Char[]));
+
+/* Convert an octal digit string to an integer value. */
+extern int otoi PROTO((Char [] ));
+
+/* Output a (possibly-formatted) string to the generated scanner. */
+extern void out PROTO((const char []));
+extern void out_dec PROTO((const char [], int));
+extern void out_dec2 PROTO((const char [], int, int));
+extern void out_hex PROTO((const char [], unsigned int));
+extern void out_line_count PROTO((const char []));
+extern void out_str PROTO((const char [], const char []));
+extern void out_str3
+ PROTO((const char [], const char [], const char [], const char []));
+extern void out_str_dec PROTO((const char [], const char [], int));
+extern void outc PROTO((int));
+extern void outn PROTO((const 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 *, size_t));
+
+
+/* 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 */
+
+/* Build the "<<EOF>>" action for the active start conditions. */
+extern void build_eof_action PROTO((void));
+
+/* 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. */
+extern void line_warning PROTO(( char[], int ));
+
+/* Write out a message, pinpointing it at the given line. */
+extern 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 format_warn PROTO((char [], char[]));
+extern void warn PROTO((char [])); /* report a warning */
+extern void yyerror PROTO((char [])); /* report a parse error */
+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 */
+
+/* Add symbol and definitions to symbol table. */
+extern int addsym PROTO((register char[], char*, int, hash_table, int));
+
+/* 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 []));
+
+/* Find symbol in symbol table. */
+extern struct hash_entry *findsym PROTO((register char[], hash_table, int ));
+
+extern void ndinstal PROTO((char[], Char[])); /* install a name definition */
+extern Char *ndlookup PROTO((char[])); /* lookup 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 */
+/* Finds a space in the table for a state to be placed. */
+extern int find_table_space PROTO((int*, int));
+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/gen.c b/usr.bin/lex/gen.c
new file mode 100644
index 0000000..08f400a
--- /dev/null
+++ b/usr.bin/lex/gen.c
@@ -0,0 +1,1625 @@
+/* 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.2 1996/06/19 20:26:10 nate 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 yyconst int %s[%d] =\n { 0,\n";
+static char C_short_decl[] = "static yyconst short int %s[%d] =\n { 0,\n";
+static char C_long_decl[] = "static yyconst long int %s[%d] =\n { 0,\n";
+static char C_state_decl[] =
+ "static yyconst 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 )
+ {
+ outc( '\t' );
+ i -= 8;
+ }
+
+ while ( i > 0 )
+ {
+ outc( ' ' );
+ --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;" );
+ outc( '\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. */
+ out_dec( "static yyconst struct yy_trans_info yy_transition[%d] =\n",
+ tblend + numecs + 1 );
+ outn( " {" );
+
+ /* 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] );
+
+ outn( " };\n" );
+
+ /* Table of pointers to start states. */
+ out_dec(
+ "static yyconst struct yy_trans_info *yy_start_state_list[%d] =\n",
+ lastsc * 2 + 1 );
+ outn( " {" ); /* } so vi doesn't get confused */
+
+ for ( i = 0; i <= lastsc * 2; ++i )
+ out_dec( " &yy_transition[%d],\n", base[i] );
+
+ dataend();
+
+ if ( useecs )
+ genecs();
+ }
+
+
+/* Generate equivalence-class tables. */
+
+void genecs()
+ {
+ register int i, j;
+ int numrows;
+
+ out_str_dec( 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];" );
+
+ outn(
+ "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];" );
+
+ if ( interactive && ! reject )
+ {
+ /* Do the guaranteed-needed backing up to figure out
+ * the match.
+ */
+ indent_puts( "if ( yy_act == 0 )" );
+ indent_up();
+ indent_puts( "{ /* have to back up */" );
+ indent_puts( "yy_cp = yy_last_accepting_cpos;" );
+ indent_puts(
+ "yy_current_state = yy_last_accepting_state;" );
+ indent_puts( "yy_act = yy_accept[yy_current_state];" );
+ indent_puts( "}" );
+ indent_down();
+ }
+ }
+ }
+
+
+/* genftbl - generate full transition table */
+
+void genftbl()
+ {
+ register int i;
+ int end_of_buffer_action = num_rules + 1;
+
+ out_str_dec( 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 */
+ out_dec( "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();
+ outc( '\n' );
+ }
+
+ indent_puts( "++yy_cp;" );
+
+ if ( num_backing_up > 0 )
+ /* { for vi */
+ indent_puts( "}" );
+
+ indent_down();
+
+ outc( '\n' );
+ indent_puts( "yy_current_state = -yy_current_state;" );
+ }
+
+ else if ( fullspd )
+ {
+ indent_puts( "{" ); /* } for vi */
+ indent_puts(
+ "register yyconst 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 )
+ {
+ outc( '\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 )
+ out_dec( "while ( yy_base[yy_current_state] != %d );\n",
+ jambase );
+ else
+ out_dec( "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 gen_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 gen_next_match() */
+ /* Only generate a definition for "yy_cp" if we'll generate code
+ * that uses it. Otherwise lint and the like complain.
+ */
+ int need_backing_up = (num_backing_up > 0 && ! reject);
+
+ if ( need_backing_up && (! nultrans || fullspd || fulltbl) )
+ /* We're going to need yy_cp lying around for the call
+ * below to gen_backing_up().
+ */
+ indent_puts( "register char *yy_cp = yy_c_buf_p;" );
+
+ outc( '\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();
+ out_dec( "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();
+ out_dec( "register int yy_c = %d;\n", NUL_ec );
+
+ indent_puts(
+ "register yyconst 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 );
+
+ do_indent();
+ out_dec( "yy_is_jam = (yy_current_state == %d);\n", jamstate );
+
+ if ( reject )
+ {
+ /* Only stack this state if it's a transition we
+ * actually make. If we stack it on a jam, then
+ * the state stack and yy_c_buf_p get out of sync.
+ */
+ indent_puts( "if ( ! yy_is_jam )" );
+ indent_up();
+ indent_puts( "*yy_state_ptr++ = yy_current_state;" );
+ indent_down();
+ }
+ }
+
+ /* 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) )
+ {
+ outc( '\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 )
+ {
+ if ( bol_needed )
+ {
+ indent_puts(
+ "yy_current_state = yy_start_state_list[yy_start + YY_AT_BOL()];" );
+ }
+ else
+ indent_puts(
+ "yy_current_state = yy_start_state_list[yy_start];" );
+ }
+
+ else
+ {
+ indent_puts( "yy_current_state = yy_start;" );
+
+ if ( bol_needed )
+ indent_puts( "yy_current_state += YY_AT_BOL();" );
+
+ 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;
+
+ 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;
+
+ out_str_dec( 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;
+
+ out_str_dec( 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 );
+
+ out_str_dec( 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;
+
+ out_str_dec( (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();
+
+ out_str_dec( (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();
+
+ out_str_dec( (total_states >= MAX_SHORT || long_align) ?
+ C_long_decl : C_short_decl,
+ "yy_nxt", tblend + 1 );
+
+ for ( i = 1; i <= tblend; ++i )
+ {
+ /* Note, the order of the following test is important.
+ * If chk[i] is 0, then nxt[i] is undefined.
+ */
+ if ( chk[i] == 0 || nxt[i] == 0 )
+ nxt[i] = jamstate; /* new state is the JAM state */
+
+ mkdata( nxt[i] );
+ }
+
+ dataend();
+
+ out_str_dec( (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();
+ out_str( fmt, arg );
+ outn( "" );
+ }
+
+
+/* Write out a string at the current indentation level, adding a final
+ * newline.
+ */
+
+void indent_puts( str )
+char str[];
+ {
+ do_indent();
+ outn( 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 && ! yytext_is_array )
+ {
+ indent_puts( "yytext_ptr -= yy_more_len; \\" );
+ indent_puts( "yyleng = (int) (yy_cp - yytext_ptr); \\" );
+ }
+
+ else
+ indent_puts( "yyleng = (int) (yy_cp - yy_bp); \\" );
+
+ /* Now also deal with copying yytext_ptr to yytext if needed. */
+ skelout();
+ if ( yytext_is_array )
+ {
+ if ( yymore_used )
+ indent_puts(
+ "if ( yyleng + yy_more_offset >= YYLMAX ) \\" );
+ else
+ indent_puts( "if ( yyleng >= YYLMAX ) \\" );
+
+ indent_up();
+ indent_puts(
+ "YY_FATAL_ERROR( \"token too large, exceeds YYLMAX\" ); \\" );
+ indent_down();
+
+ if ( yymore_used )
+ {
+ indent_puts(
+"yy_flex_strncpy( &yytext[yy_more_offset], yytext_ptr, yyleng + 1 ); \\" );
+ indent_puts( "yyleng += yy_more_offset; \\" );
+ indent_puts(
+ "yy_prev_more_offset = yy_more_offset; \\" );
+ indent_puts( "yy_more_offset = 0; \\" );
+ }
+ else
+ {
+ indent_puts(
+ "yy_flex_strncpy( yytext, yytext_ptr, yyleng + 1 ); \\" );
+ }
+ }
+
+ set_indent( 0 );
+
+ skelout();
+
+
+ out_dec( "#define YY_NUM_RULES %d\n", num_rules );
+ out_dec( "#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 )
+ {
+ out_str_dec( C_state_decl, "yy_NUL_trans", lastdfa + 1 );
+
+ for ( i = 1; i <= lastdfa; ++i )
+ {
+ if ( fullspd )
+ out_dec( " &yy_transition[%d],\n", base[i] );
+ else
+ mkdata( nultrans[i] );
+ }
+
+ dataend();
+ }
+
+ if ( ddebug )
+ { /* Spit out table mapping rules to line numbers. */
+ if ( ! C_plus_plus )
+ {
+ indent_puts( "extern int yy_flex_debug;" );
+ indent_puts( "int yy_flex_debug = 1;\n" );
+ }
+
+ out_str_dec( 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 )
+ {
+ outn(
+ "static yy_state_type yy_state_buf[YY_BUF_SIZE + 2], *yy_state_ptr;" );
+ outn( "static char *yy_full_match;" );
+ outn( "static int yy_lp;" );
+ }
+
+ if ( variable_trailing_context_rules )
+ {
+ if ( ! C_plus_plus )
+ {
+ outn(
+ "static int yy_looking_for_trail_begin = 0;" );
+ outn( "static int yy_full_lp;" );
+ outn( "static int *yy_full_state;" );
+ }
+
+ out_hex( "#define YY_TRAILING_MASK 0x%x\n",
+ (unsigned int) YY_TRAILING_MASK );
+ out_hex( "#define YY_TRAILING_HEAD_MASK 0x%x\n",
+ (unsigned int) YY_TRAILING_HEAD_MASK );
+ }
+
+ outn( "#define REJECT \\" );
+ outn( "{ \\" ); /* } for vi */
+ outn(
+ "*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ \\" );
+ outn(
+ "yy_cp = yy_full_match; /* restore poss. backed-over text */ \\" );
+
+ if ( variable_trailing_context_rules )
+ {
+ outn(
+ "yy_lp = yy_full_lp; /* restore orig. accepting pos. */ \\" );
+ outn(
+ "yy_state_ptr = yy_full_state; /* restore orig. state */ \\" );
+ outn(
+ "yy_current_state = *yy_state_ptr; /* restore curr. state */ \\" );
+ }
+
+ outn( "++yy_lp; \\" );
+ outn( "goto find_rule; \\" );
+ /* { for vi */
+ outn( "}" );
+ }
+
+ else
+ {
+ outn(
+ "/* The intent behind this definition is that it'll catch" );
+ outn( " * any uses of REJECT which flex missed." );
+ outn( " */" );
+ outn( "#define REJECT reject_used_but_not_detected" );
+ }
+
+ if ( yymore_used )
+ {
+ if ( ! C_plus_plus )
+ {
+ if ( yytext_is_array )
+ {
+ indent_puts( "static int yy_more_offset = 0;" );
+ indent_puts(
+ "static int yy_prev_more_offset = 0;" );
+ }
+ else
+ {
+ indent_puts( "static int yy_more_flag = 0;" );
+ indent_puts( "static int yy_more_len = 0;" );
+ }
+ }
+
+ if ( yytext_is_array )
+ {
+ indent_puts(
+ "#define yymore() (yy_more_offset = yy_flex_strlen( yytext ))" );
+ indent_puts( "#define YY_NEED_STRLEN" );
+ indent_puts( "#define YY_MORE_ADJ 0" );
+ indent_puts( "#define YY_RESTORE_YY_MORE_OFFSET \\" );
+ indent_up();
+ indent_puts( "{ \\" );
+ indent_puts( "yy_more_offset = yy_prev_more_offset; \\" );
+ indent_puts( "yyleng -= yy_more_offset; \\" );
+ indent_puts( "}" );
+ indent_down();
+ }
+ else
+ {
+ indent_puts( "#define yymore() (yy_more_flag = 1)" );
+ indent_puts( "#define YY_MORE_ADJ yy_more_len" );
+ indent_puts( "#define YY_RESTORE_YY_MORE_OFFSET" );
+ }
+ }
+
+ else
+ {
+ indent_puts( "#define yymore() yymore_used_but_not_detected" );
+ indent_puts( "#define YY_MORE_ADJ 0" );
+ indent_puts( "#define YY_RESTORE_YY_MORE_OFFSET" );
+ }
+
+ if ( ! C_plus_plus )
+ {
+ if ( yytext_is_array )
+ {
+ outn( "#ifndef YYLMAX" );
+ outn( "#define YYLMAX 8192" );
+ outn( "#endif\n" );
+ outn( "char yytext[YYLMAX];" );
+ outn( "char *yytext_ptr;" );
+ }
+
+ else
+ outn( "char *yytext;" );
+ }
+
+ out( &action_array[defs1_offset] );
+
+ line_directive_out( stdout, 0 );
+
+ skelout();
+
+ if ( ! C_plus_plus )
+ {
+ if ( use_read )
+ {
+ outn(
+"\tif ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \\" );
+ outn(
+ "\t\tYY_FATAL_ERROR( \"input in flex scanner failed\" );" );
+ }
+
+ else
+ {
+ outn(
+ "\tif ( yy_current_buffer->yy_is_interactive ) \\" );
+ outn( "\t\t{ \\" );
+ outn( "\t\tint c = '*', n; \\" );
+ outn( "\t\tfor ( n = 0; n < max_size && \\" );
+ outn( "\t\t\t (c = getc( yyin )) != EOF && c != '\\n'; ++n ) \\" );
+ outn( "\t\t\tbuf[n] = (char) c; \\" );
+ outn( "\t\tif ( c == '\\n' ) \\" );
+ outn( "\t\t\tbuf[n++] = (char) c; \\" );
+ outn( "\t\tif ( c == EOF && ferror( yyin ) ) \\" );
+ outn(
+ "\t\t\tYY_FATAL_ERROR( \"input in flex scanner failed\" ); \\" );
+ outn( "\t\tresult = n; \\" );
+ outn( "\t\t} \\" );
+ outn(
+ "\telse if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \\" );
+ outn( "\t\t && ferror( yyin ) ) \\" );
+ outn(
+ "\t\tYY_FATAL_ERROR( \"input in flex scanner failed\" );" );
+ }
+ }
+
+ skelout();
+
+ indent_puts( "#define YY_RULE_SETUP \\" );
+ indent_up();
+ if ( bol_needed )
+ {
+ indent_puts( "if ( yyleng > 0 ) \\" );
+ indent_up();
+ indent_puts( "yy_current_buffer->yy_at_bol = \\" );
+ indent_puts( "\t\t(yytext[yyleng - 1] == '\\n'); \\" );
+ indent_down();
+ }
+ indent_puts( "YY_USER_ACTION" );
+ indent_down();
+
+ skelout();
+
+ /* Copy prolog to output file. */
+ out( &action_array[prolog_offset] );
+
+ line_directive_out( stdout, 0 );
+
+ skelout();
+
+ set_indent( 2 );
+
+ if ( yymore_used && ! yytext_is_array )
+ {
+ indent_puts( "yy_more_len = 0;" );
+ indent_puts( "if ( yy_more_flag )" );
+ indent_up();
+ indent_puts( "{" );
+ indent_puts( "yy_more_len = yy_c_buf_p - yytext_ptr;" );
+ indent_puts( "yy_more_flag = 0;" );
+ indent_puts( "}" );
+ indent_down();
+ }
+
+ skelout();
+
+ gen_start_state();
+
+ /* Note, don't use any indentation. */
+ outn( "yy_match:" );
+ gen_next_match();
+
+ skelout();
+ set_indent( 2 );
+ gen_find_action();
+
+ skelout();
+ if ( do_yylineno )
+ {
+ 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( C_plus_plus ?
+ "cerr << \"--scanner backing up\\n\";" :
+ "fprintf( stderr, \"--scanner backing up\\n\" );" );
+ indent_down();
+
+ do_indent();
+ out_dec( "else if ( yy_act < %d )\n", num_rules );
+ indent_up();
+
+ if ( C_plus_plus )
+ {
+ indent_puts(
+ "cerr << \"--accepting rule at line \" << yy_rule_linenum[yy_act] <<" );
+ indent_puts(
+ " \"(\\\"\" << yytext << \"\\\")\\n\";" );
+ }
+ else
+ {
+ indent_puts(
+ "fprintf( stderr, \"--accepting rule at line %d (\\\"%s\\\")\\n\"," );
+
+ indent_puts(
+ " yy_rule_linenum[yy_act], yytext );" );
+ }
+
+ indent_down();
+
+ do_indent();
+ out_dec( "else if ( yy_act == %d )\n", num_rules );
+ indent_up();
+
+ if ( C_plus_plus )
+ {
+ indent_puts(
+"cerr << \"--accepting default rule (\\\"\" << yytext << \"\\\")\\n\";" );
+ }
+ else
+ {
+ indent_puts(
+ "fprintf( stderr, \"--accepting default rule (\\\"%s\\\")\\n\"," );
+ indent_puts( " yytext );" );
+ }
+
+ indent_down();
+
+ do_indent();
+ out_dec( "else if ( yy_act == %d )\n", num_rules + 1 );
+ indent_up();
+
+ indent_puts( C_plus_plus ?
+ "cerr << \"--(end of buffer or a NUL)\\n\";" :
+ "fprintf( stderr, \"--(end of buffer or a NUL)\\n\" );" );
+
+ indent_down();
+
+ do_indent();
+ outn( "else" );
+ indent_up();
+
+ if ( C_plus_plus )
+ {
+ indent_puts(
+ "cerr << \"--EOF (start condition \" << YY_START << \")\\n\";" );
+ }
+ else
+ {
+ 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();
+ out( &action_array[action_offset] );
+
+ line_directive_out( stdout, 0 );
+
+ /* generate cases for any missing EOF rules */
+ for ( i = 1; i <= lastsc; ++i )
+ if ( ! sceof[i] )
+ {
+ do_indent();
+ out_str( "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( 4 );
+
+ 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();
+
+ gen_start_state();
+
+ set_indent( 2 );
+ skelout();
+ gen_next_state( true );
+
+ set_indent( 1 );
+ skelout();
+ gen_NUL_trans();
+
+ skelout();
+ if ( do_yylineno )
+ { /* update yylineno inside of unput() */
+ indent_puts( "if ( c == '\\n' )" );
+ indent_up();
+ indent_puts( "--yylineno;" );
+ indent_down();
+ }
+
+ skelout();
+ /* Update BOL and yylineno inside of input(). */
+ if ( bol_needed )
+ {
+ indent_puts( "yy_current_buffer->yy_at_bol = (c == '\\n');" );
+ if ( do_yylineno )
+ {
+ indent_puts( "if ( yy_current_buffer->yy_at_bol )" );
+ indent_up();
+ indent_puts( "++yylineno;" );
+ indent_down();
+ }
+ }
+
+ else if ( do_yylineno )
+ {
+ indent_puts( "if ( c == '\\n' )" );
+ indent_up();
+ indent_puts( "++yylineno;" );
+ indent_down();
+ }
+
+ skelout();
+
+ /* Copy remainder of input to output. */
+
+ line_directive_out( stdout, 1 );
+
+ 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..f5e9dd1
--- /dev/null
+++ b/usr.bin/lex/initscan.c
@@ -0,0 +1,3697 @@
+#line 2 "scan.c"
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header: /home/ncvs/src/usr.bin/lex/initscan.c,v 1.3 1996/06/19 20:47:13 nate Exp $
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+#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 */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#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. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* 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". */
+#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;
+
+#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_RESTORE_YY_MORE_OFFSET \
+ 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 )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+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.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* 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 we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ /* 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;
+
+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 ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+typedef unsigned char YY_CHAR;
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+typedef int yy_state_type;
+extern char *yytext;
+#define yytext_ptr yytext
+
+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(( yyconst 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 = (int) (yy_cp - yy_bp); \
+ yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 165
+#define YY_END_OF_BUFFER 166
+static yyconst short int yy_accept[769] =
+ { 0,
+ 0, 0, 0, 0, 87, 87, 163, 163, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 166, 164,
+ 7, 18, 164, 16, 1, 17, 164, 164, 164, 164,
+ 15, 108, 100, 101, 108, 93, 108, 107, 108, 108,
+ 108, 107, 99, 89, 108, 108, 91, 92, 87, 88,
+ 87, 86, 85, 86, 86, 163, 163, 28, 29, 28,
+ 28, 28, 28, 28, 28, 31, 30, 32, 31, 113,
+ 109, 110, 112, 114, 141, 142, 141, 139, 138, 140,
+
+ 115, 117, 115, 116, 115, 120, 120, 120, 120, 122,
+ 124, 122, 122, 122, 122, 123, 151, 155, 151, 154,
+ 156, 156, 152, 152, 152, 149, 150, 164, 82, 164,
+ 21, 22, 21, 20, 157, 159, 157, 160, 161, 147,
+ 147, 148, 147, 147, 147, 147, 147, 147, 147, 81,
+ 34, 33, 81, 81, 81, 81, 35, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 26, 23, 26, 24,
+ 7, 18, 0, 16, 1, 17, 0, 0, 0, 14,
+ 8, 0, 0, 0, 0, 4, 5, 0, 2, 15,
+
+ 100, 101, 0, 0, 0, 95, 0, 0, 105, 105,
+ 0, 162, 162, 162, 94, 0, 99, 89, 0, 0,
+ 0, 91, 92, 104, 90, 0, 87, 88, 86, 85,
+ 85, 83, 84, 163, 163, 28, 29, 28, 28, 28,
+ 28, 31, 30, 32, 111, 112, 142, 138, 117, 0,
+ 118, 119, 124, 121, 151, 155, 0, 153, 0, 144,
+ 152, 152, 152, 0, 82, 0, 21, 22, 21, 19,
+ 157, 159, 158, 147, 147, 147, 148, 143, 147, 147,
+ 147, 34, 33, 0, 80, 0, 0, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
+
+ 81, 81, 81, 36, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 0, 25, 24, 0, 14, 8,
+ 0, 12, 0, 0, 0, 0, 0, 4, 5, 0,
+ 6, 0, 96, 0, 97, 0, 0, 105, 105, 0,
+ 105, 105, 105, 162, 162, 0, 106, 90, 98, 0,
+ 104, 0, 83, 84, 28, 28, 28, 27, 28, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 152, 152, 143, 143, 147, 147, 0, 0, 81,
+ 81, 81, 81, 81, 44, 81, 81, 81, 49, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
+
+ 81, 81, 81, 81, 81, 81, 81, 81, 0, 81,
+ 81, 81, 81, 0, 0, 0, 12, 0, 0, 0,
+ 0, 0, 0, 4, 5, 0, 105, 105, 105, 105,
+ 105, 105, 162, 0, 0, 28, 28, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 152, 152, 147, 147, 37, 38, 81, 81, 81, 81,
+ 81, 81, 81, 81, 50, 51, 81, 81, 81, 55,
+ 81, 81, 81, 81, 81, 81, 60, 81, 81, 81,
+ 81, 81, 81, 67, 0, 0, 0, 81, 81, 81,
+ 81, 0, 13, 0, 0, 0, 0, 0, 0, 105,
+
+ 105, 105, 105, 105, 105, 0, 0, 28, 28, 137,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 152, 152, 147, 147, 39, 81, 41, 81,
+ 43, 81, 81, 81, 47, 81, 52, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 62, 81, 81, 65,
+ 81, 0, 0, 0, 0, 81, 81, 81, 81, 3,
+ 0, 0, 0, 0, 105, 105, 105, 0, 0, 28,
+ 28, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 145, 146, 145, 146, 81, 42, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
+
+ 81, 78, 61, 81, 64, 81, 0, 0, 0, 0,
+ 81, 81, 69, 70, 0, 10, 0, 11, 0, 103,
+ 0, 102, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 81, 81, 81, 45, 81, 48,
+ 81, 81, 81, 81, 77, 81, 59, 63, 66, 0,
+ 0, 0, 0, 79, 81, 0, 102, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 81,
+ 81, 81, 46, 81, 81, 56, 81, 81, 0, 0,
+ 0, 0, 68, 0, 9, 0, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 134, 135, 0, 81, 81,
+
+ 81, 81, 81, 81, 81, 0, 0, 0, 0, 0,
+ 136, 81, 81, 81, 81, 54, 81, 81, 0, 0,
+ 0, 0, 0, 0, 81, 81, 81, 53, 81, 58,
+ 0, 0, 0, 0, 0, 0, 81, 81, 81, 81,
+ 72, 0, 0, 0, 0, 73, 81, 81, 81, 81,
+ 71, 0, 75, 0, 81, 81, 81, 74, 76, 81,
+ 81, 81, 81, 81, 81, 57, 40, 0
+ } ;
+
+static yyconst 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, 6, 7, 8, 1, 9, 10,
+ 10, 11, 12, 13, 14, 10, 15, 16, 16, 16,
+ 16, 16, 16, 16, 17, 18, 19, 20, 1, 21,
+ 22, 23, 10, 1, 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, 47,
+ 26, 27, 28, 29, 30, 1, 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, 47, 56, 57, 58, 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 yyconst int yy_meta[59] =
+ { 0,
+ 1, 1, 2, 1, 3, 1, 1, 1, 4, 1,
+ 5, 6, 1, 7, 4, 8, 8, 8, 8, 1,
+ 1, 1, 1, 9, 10, 1, 11, 12, 1, 13,
+ 14, 14, 14, 14, 14, 14, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 4, 1, 16
+ } ;
+
+static yyconst short int yy_base[858] =
+ { 0,
+ 0, 58, 115, 172, 120, 129, 2712, 2711, 230, 2705,
+ 136, 141, 288, 0, 2683, 2682, 144, 151, 185, 191,
+ 178, 188, 344, 347, 375, 0, 125, 131, 147, 216,
+ 431, 434, 461, 0, 519, 0, 205, 349, 2710, 2716,
+ 353, 2716, 2706, 0, 360, 2716, 2705, 144, 570, 2696,
+ 0, 2716, 577, 2716, 2703, 2716, 438, 2716, 2684, 126,
+ 149, 427, 591, 2716, 2701, 141, 2682, 2716, 0, 2716,
+ 2699, 0, 2699, 2697, 155, 2696, 2716, 0, 2716, 2695,
+ 2716, 0, 2662, 2641, 2637, 0, 2692, 2716, 2690, 2716,
+ 2716, 2663, 0, 2716, 2716, 2716, 2688, 2716, 431, 2716,
+
+ 2716, 2716, 2687, 2716, 567, 2716, 2669, 571, 164, 2716,
+ 2716, 2685, 0, 2667, 573, 2716, 0, 2716, 2683, 2716,
+ 573, 2674, 0, 2649, 2628, 2716, 2716, 222, 2716, 356,
+ 448, 2716, 450, 2667, 0, 2716, 2678, 2716, 0, 0,
+ 198, 2716, 2677, 2621, 2716, 2667, 0, 2642, 2621, 2716,
+ 2673, 2716, 2671, 2668, 2640, 2639, 2716, 544, 2639, 579,
+ 2634, 2635, 318, 0, 2623, 2631, 424, 562, 2614, 587,
+ 2629, 2613, 2618, 2626, 2629, 2604, 2716, 2716, 2653, 612,
+ 634, 2716, 2654, 0, 637, 2716, 2653, 600, 2616, 0,
+ 0, 641, 647, 651, 669, 0, 0, 453, 2716, 0,
+
+ 672, 2716, 2651, 2597, 605, 2716, 2649, 2616, 620, 657,
+ 645, 2716, 662, 0, 2716, 2592, 688, 2716, 2646, 2592,
+ 2636, 2625, 2716, 0, 2716, 2610, 0, 2716, 0, 0,
+ 2642, 0, 0, 2640, 2716, 0, 2716, 0, 2602, 2598,
+ 745, 0, 2638, 2716, 2716, 0, 2716, 688, 2716, 773,
+ 2716, 2716, 2716, 2716, 0, 2716, 673, 2716, 0, 2716,
+ 0, 2599, 2595, 690, 2716, 698, 707, 2716, 709, 2716,
+ 0, 2716, 2716, 0, 596, 2579, 2716, 827, 0, 2596,
+ 2592, 2632, 2716, 2628, 2716, 2593, 2592, 0, 642, 2582,
+ 563, 2617, 2579, 620, 2578, 2577, 2583, 669, 2570, 2584,
+
+ 2572, 0, 2569, 2716, 2570, 2571, 2579, 2582, 685, 125,
+ 2570, 2567, 2566, 688, 2608, 2716, 716, 2568, 0, 0,
+ 720, 2716, 2608, 884, 2562, 2559, 2569, 0, 0, 723,
+ 2716, 739, 2716, 805, 2716, 808, 2562, 787, 869, 876,
+ 930, 881, 973, 800, 0, 2548, 2716, 2716, 2716, 2570,
+ 0, 2559, 0, 0, 2568, 2557, 0, 2716, 0, 1009,
+ 2581, 678, 870, 871, 874, 879, 913, 992, 974, 1013,
+ 885, 2565, 2554, 0, 1067, 2563, 2552, 2546, 2545, 2557,
+ 2562, 2561, 2550, 2557, 0, 2554, 2537, 2556, 0, 2536,
+ 2543, 2533, 2548, 2568, 2537, 2549, 2544, 2542, 2541, 2532,
+
+ 2539, 2540, 2538, 2539, 578, 2520, 2538, 2525, 860, 2526,
+ 2528, 2521, 2517, 2529, 817, 1044, 2716, 822, 1095, 914,
+ 2532, 2523, 2517, 0, 0, 2524, 1102, 1025, 1142, 2539,
+ 1028, 1163, 2716, 2513, 2521, 2523, 2507, 0, 2526, 1058,
+ 891, 1014, 1019, 894, 1038, 1080, 1072, 1086, 1083, 1081,
+ 2520, 2504, 2518, 2502, 2716, 2716, 2505, 2493, 2492, 2495,
+ 2507, 1148, 2507, 2492, 0, 0, 2492, 2493, 2507, 0,
+ 2525, 2490, 2498, 2522, 2485, 2495, 0, 2500, 2491, 2487,
+ 2479, 2479, 2483, 0, 875, 2494, 2481, 2494, 2480, 2475,
+ 2491, 2519, 2716, 920, 999, 2465, 2474, 2468, 2494, 2496,
+
+ 1105, 1184, 1081, 902, 969, 2479, 2491, 2463, 2477, 2716,
+ 165, 1090, 1144, 1143, 1147, 1163, 1095, 1145, 1037, 1085,
+ 1150, 1173, 2461, 2475, 2459, 2473, 0, 2458, 0, 2460,
+ 0, 1165, 2454, 2469, 0, 2461, 0, 2471, 2410, 2414,
+ 2434, 2400, 2393, 2405, 2385, 2382, 0, 2383, 2335, 0,
+ 2335, 2330, 2326, 2309, 2278, 2259, 2269, 2268, 2256, 2297,
+ 1046, 2238, 2242, 2253, 1179, 1142, 1145, 2247, 2246, 0,
+ 0, 1191, 1192, 1172, 1201, 1202, 1204, 1205, 1206, 1207,
+ 1209, 1210, 1208, 0, 0, 0, 0, 2254, 0, 2221,
+ 2229, 2218, 2208, 2200, 2209, 2198, 2195, 2165, 2168, 2149,
+
+ 2132, 0, 0, 2129, 0, 2139, 2143, 2134, 2124, 2137,
+ 2117, 2116, 0, 0, 1228, 2716, 1232, 2716, 2111, 2716,
+ 2117, 2716, 2115, 2114, 2108, 2107, 2106, 2103, 2102, 2098,
+ 2095, 2063, 2047, 1213, 2012, 1986, 1975, 0, 1954, 0,
+ 1947, 1950, 1941, 1945, 0, 1942, 0, 0, 0, 1938,
+ 1940, 1934, 1905, 0, 1872, 1234, 2716, 1888, 1882, 1881,
+ 1864, 1848, 1832, 1828, 1827, 1826, 1823, 1806, 1809, 1784,
+ 1787, 1772, 0, 1781, 1786, 0, 1766, 1767, 1759, 1744,
+ 1213, 1736, 0, 1236, 2716, 1245, 2716, 2716, 2716, 2716,
+ 2716, 2716, 2716, 2716, 2716, 2716, 2716, 1750, 1727, 1720,
+
+ 1701, 1687, 1670, 1681, 1667, 1679, 1659, 689, 1658, 1671,
+ 2716, 1657, 1627, 1621, 1635, 0, 1603, 1596, 1595, 1608,
+ 1602, 1587, 1586, 1583, 1581, 1587, 1555, 0, 1547, 0,
+ 1527, 1507, 1520, 1503, 1483, 1482, 1485, 1443, 1440, 1228,
+ 2716, 1225, 1224, 1206, 1210, 2716, 1213, 1202, 1018, 948,
+ 2716, 945, 2716, 884, 780, 771, 779, 2716, 2716, 689,
+ 673, 581, 408, 318, 86, 0, 0, 2716, 1263, 1279,
+ 1295, 1311, 1327, 1343, 1359, 1375, 1391, 1407, 1423, 1439,
+ 1455, 1471, 1481, 1496, 1505, 1520, 1536, 1545, 1560, 1576,
+ 1592, 1608, 1624, 1634, 1649, 1659, 1674, 1690, 1706, 1718,
+
+ 1728, 1743, 1759, 1775, 1791, 1807, 1817, 1832, 1843, 1236,
+ 1858, 1874, 1890, 1898, 1905, 1920, 1936, 1952, 1968, 1977,
+ 1985, 2001, 2017, 2033, 2049, 2065, 2081, 2097, 2113, 2123,
+ 2138, 2148, 2155, 2170, 2182, 2192, 2207, 2223, 2239, 2255,
+ 2265, 2280, 2291, 2306, 2322, 2338, 2354, 2364, 2373, 2388,
+ 2404, 2420, 2429, 2437, 2453, 2469, 2485
+ } ;
+
+static yyconst short int yy_def[858] =
+ { 0,
+ 768, 768, 769, 769, 770, 771, 772, 772, 768, 9,
+ 773, 773, 768, 13, 774, 774, 775, 775, 776, 776,
+ 777, 777, 778, 778, 768, 25, 779, 779, 780, 780,
+ 781, 781, 768, 33, 768, 35, 782, 782, 768, 768,
+ 768, 768, 768, 783, 768, 768, 768, 768, 784, 768,
+ 785, 768, 768, 768, 768, 768, 768, 768, 768, 786,
+ 787, 788, 768, 768, 768, 768, 768, 768, 789, 768,
+ 789, 790, 791, 790, 790, 792, 768, 793, 768, 793,
+ 768, 794, 794, 794, 793, 795, 768, 768, 795, 768,
+ 768, 768, 796, 768, 768, 768, 768, 768, 768, 768,
+
+ 768, 768, 768, 768, 787, 768, 768, 787, 797, 768,
+ 768, 768, 798, 768, 787, 768, 799, 768, 799, 768,
+ 800, 768, 801, 801, 801, 768, 768, 802, 768, 802,
+ 803, 768, 803, 768, 804, 768, 804, 768, 805, 806,
+ 806, 768, 806, 806, 768, 806, 807, 807, 807, 768,
+ 768, 768, 768, 808, 768, 768, 768, 809, 809, 809,
+ 809, 809, 809, 809, 809, 809, 809, 810, 809, 809,
+ 809, 809, 809, 809, 809, 809, 768, 768, 811, 768,
+ 768, 768, 768, 783, 768, 768, 768, 768, 768, 812,
+ 813, 768, 768, 768, 768, 814, 815, 816, 768, 785,
+
+ 768, 768, 768, 768, 817, 768, 768, 768, 818, 818,
+ 819, 768, 768, 820, 768, 821, 768, 768, 768, 768,
+ 768, 768, 768, 822, 768, 768, 823, 768, 824, 825,
+ 825, 826, 827, 828, 768, 829, 768, 830, 830, 830,
+ 768, 831, 768, 768, 768, 832, 768, 768, 768, 833,
+ 768, 768, 768, 768, 834, 768, 835, 768, 835, 768,
+ 836, 836, 836, 837, 768, 837, 838, 768, 838, 768,
+ 839, 768, 768, 840, 840, 840, 768, 768, 841, 841,
+ 841, 768, 768, 842, 768, 768, 768, 843, 843, 843,
+ 843, 843, 843, 843, 843, 843, 843, 843, 843, 843,
+
+ 843, 843, 843, 768, 843, 843, 843, 843, 843, 843,
+ 843, 843, 843, 843, 844, 768, 768, 768, 845, 846,
+ 847, 768, 768, 768, 768, 768, 768, 848, 849, 850,
+ 768, 850, 768, 851, 768, 851, 768, 852, 852, 852,
+ 768, 852, 852, 768, 853, 854, 768, 768, 768, 768,
+ 855, 768, 826, 827, 830, 830, 241, 768, 241, 241,
+ 833, 833, 833, 833, 833, 833, 833, 833, 833, 833,
+ 833, 836, 836, 278, 278, 841, 841, 768, 768, 843,
+ 843, 843, 843, 843, 843, 843, 843, 843, 843, 843,
+ 843, 843, 843, 843, 843, 843, 843, 843, 843, 843,
+
+ 843, 843, 843, 843, 843, 843, 843, 843, 768, 843,
+ 843, 843, 843, 768, 847, 847, 768, 847, 847, 768,
+ 768, 768, 768, 848, 849, 768, 341, 852, 343, 341,
+ 852, 343, 768, 768, 768, 830, 830, 360, 768, 833,
+ 833, 833, 833, 833, 833, 833, 833, 833, 833, 833,
+ 836, 836, 841, 841, 768, 768, 843, 843, 843, 843,
+ 843, 843, 843, 843, 843, 843, 843, 843, 843, 843,
+ 843, 843, 843, 843, 843, 843, 843, 843, 843, 843,
+ 843, 843, 843, 843, 768, 768, 768, 843, 843, 843,
+ 843, 768, 768, 847, 847, 768, 768, 768, 768, 427,
+
+ 852, 343, 852, 852, 852, 768, 768, 830, 830, 768,
+ 833, 833, 833, 833, 833, 833, 833, 833, 833, 833,
+ 833, 833, 836, 836, 841, 841, 843, 843, 843, 843,
+ 843, 843, 843, 843, 843, 843, 843, 843, 843, 843,
+ 843, 843, 843, 843, 843, 843, 843, 843, 843, 843,
+ 843, 768, 768, 768, 768, 843, 843, 843, 843, 768,
+ 856, 768, 768, 768, 852, 852, 852, 768, 768, 830,
+ 830, 833, 833, 833, 833, 833, 833, 833, 833, 833,
+ 833, 833, 833, 836, 836, 841, 841, 843, 843, 843,
+ 843, 843, 843, 843, 843, 843, 843, 843, 843, 843,
+
+ 843, 843, 843, 843, 843, 843, 768, 768, 768, 768,
+ 843, 843, 843, 843, 856, 768, 856, 768, 768, 768,
+ 768, 768, 833, 833, 833, 833, 833, 833, 833, 833,
+ 833, 833, 833, 833, 843, 843, 843, 843, 843, 843,
+ 843, 843, 843, 843, 843, 843, 843, 843, 843, 768,
+ 768, 768, 768, 843, 843, 857, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 833, 843,
+ 843, 843, 843, 843, 843, 843, 843, 843, 768, 768,
+ 768, 768, 843, 857, 768, 857, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 843, 843,
+
+ 843, 843, 843, 843, 843, 768, 768, 768, 768, 768,
+ 768, 843, 843, 843, 843, 843, 843, 843, 768, 768,
+ 768, 768, 768, 768, 843, 843, 843, 843, 843, 843,
+ 768, 768, 768, 768, 768, 768, 843, 843, 843, 843,
+ 768, 768, 768, 768, 768, 768, 843, 843, 843, 843,
+ 768, 768, 768, 768, 843, 843, 843, 768, 768, 843,
+ 843, 843, 843, 843, 843, 843, 843, 0, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768
+ } ;
+
+static yyconst short int yy_nxt[2775] =
+ { 0,
+ 40, 41, 42, 43, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 44, 44, 40, 40, 40, 40, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 40, 40, 40, 40, 45,
+ 46, 47, 40, 48, 40, 49, 40, 40, 40, 40,
+ 40, 40, 50, 40, 40, 40, 40, 40, 40, 40,
+ 40, 51, 51, 40, 40, 40, 40, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+
+ 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 40, 40, 40, 53, 54, 55, 56,
+ 767, 57, 70, 71, 58, 58, 58, 129, 130, 58,
+ 73, 70, 74, 129, 130, 59, 75, 87, 88, 89,
+ 60, 61, 87, 88, 89, 188, 96, 97, 224, 132,
+ 133, 210, 211, 96, 97, 404, 98, 134, 405, 99,
+ 99, 99, 99, 98, 213, 213, 99, 99, 99, 99,
+ 62, 58, 58, 63, 64, 65, 56, 252, 57, 66,
+ 40, 58, 58, 58, 439, 189, 58, 102, 103, 104,
+ 40, 252, 67, 102, 103, 104, 225, 60, 61, 275,
+
+ 68, 100, 214, 107, 108, 276, 109, 178, 100, 179,
+ 232, 105, 233, 107, 108, 572, 109, 105, 132, 133,
+ 180, 180, 180, 180, 265, 266, 134, 62, 58, 58,
+ 78, 78, 79, 80, 78, 78, 78, 78, 78, 78,
+ 81, 78, 78, 78, 78, 78, 78, 78, 78, 78,
+ 78, 78, 78, 82, 82, 78, 78, 78, 78, 82,
+ 82, 82, 82, 82, 82, 82, 82, 82, 82, 82,
+ 82, 82, 82, 82, 82, 82, 82, 83, 82, 82,
+ 82, 82, 82, 82, 84, 78, 78, 78, 90, 90,
+ 40, 90, 90, 90, 90, 90, 90, 90, 91, 90,
+
+ 91, 90, 90, 90, 90, 90, 90, 90, 90, 90,
+ 92, 93, 93, 90, 90, 90, 90, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 90, 90, 90, 111, 112, 296, 111,
+ 112, 178, 766, 179, 181, 182, 183, 113, 265, 266,
+ 113, 185, 186, 187, 180, 180, 180, 180, 297, 114,
+ 115, 116, 114, 115, 116, 117, 117, 118, 119, 120,
+ 117, 117, 117, 121, 117, 117, 117, 117, 117, 122,
+ 117, 117, 117, 117, 117, 117, 117, 117, 123, 123,
+
+ 117, 117, 117, 117, 123, 123, 123, 123, 123, 123,
+ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 123, 123, 124, 123, 123, 123, 123, 123, 123, 125,
+ 126, 117, 127, 136, 137, 138, 136, 137, 138, 206,
+ 206, 207, 215, 215, 215, 215, 248, 248, 248, 248,
+ 268, 269, 268, 269, 300, 331, 332, 139, 301, 765,
+ 139, 140, 141, 142, 143, 140, 140, 140, 144, 140,
+ 140, 145, 140, 140, 140, 146, 140, 140, 140, 140,
+ 140, 140, 140, 140, 147, 147, 140, 140, 140, 140,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+
+ 147, 147, 147, 147, 147, 147, 147, 147, 148, 147,
+ 147, 147, 147, 147, 147, 149, 140, 140, 140, 150,
+ 151, 152, 153, 154, 150, 150, 150, 150, 150, 150,
+ 150, 150, 150, 150, 150, 155, 156, 150, 150, 150,
+ 157, 150, 150, 150, 150, 150, 150, 150, 150, 158,
+ 159, 160, 161, 162, 163, 164, 164, 165, 164, 164,
+ 166, 167, 168, 169, 170, 164, 171, 172, 164, 173,
+ 174, 175, 164, 176, 150, 150, 150, 191, 201, 202,
+ 203, 258, 213, 213, 204, 289, 213, 213, 213, 213,
+ 292, 290, 217, 218, 219, 383, 303, 275, 220, 259,
+
+ 192, 188, 193, 276, 193, 221, 304, 335, 336, 293,
+ 193, 222, 384, 193, 194, 195, 480, 193, 196, 223,
+ 214, 306, 481, 197, 214, 198, 214, 317, 317, 317,
+ 317, 307, 764, 205, 308, 181, 182, 183, 185, 186,
+ 187, 189, 321, 322, 323, 339, 340, 205, 321, 322,
+ 323, 387, 321, 322, 323, 388, 324, 324, 324, 324,
+ 342, 342, 324, 324, 324, 324, 324, 324, 324, 324,
+ 321, 322, 323, 201, 202, 203, 341, 344, 344, 204,
+ 380, 258, 339, 340, 324, 324, 324, 324, 325, 217,
+ 218, 219, 265, 266, 381, 220, 326, 439, 343, 259,
+
+ 265, 266, 221, 248, 248, 248, 248, 673, 222, 268,
+ 269, 268, 269, 327, 392, 402, 223, 409, 393, 440,
+ 410, 416, 417, 418, 403, 331, 332, 763, 205, 411,
+ 412, 317, 317, 317, 317, 419, 419, 419, 419, 721,
+ 413, 331, 332, 722, 205, 357, 357, 358, 359, 357,
+ 357, 357, 357, 357, 357, 360, 357, 357, 357, 357,
+ 357, 357, 357, 357, 357, 357, 357, 357, 360, 360,
+ 357, 357, 357, 357, 360, 360, 360, 360, 360, 360,
+ 360, 360, 360, 360, 360, 360, 360, 360, 360, 360,
+ 360, 360, 360, 360, 360, 360, 360, 360, 360, 360,
+
+ 357, 357, 357, 362, 363, 364, 365, 335, 336, 366,
+ 335, 336, 339, 340, 367, 212, 212, 762, 368, 493,
+ 494, 369, 761, 370, 417, 494, 371, 374, 374, 760,
+ 374, 374, 374, 374, 374, 374, 374, 375, 374, 374,
+ 374, 374, 374, 374, 374, 374, 374, 374, 374, 374,
+ 375, 375, 374, 374, 374, 374, 375, 375, 375, 375,
+ 375, 375, 375, 375, 375, 375, 375, 375, 375, 375,
+ 375, 375, 375, 375, 375, 375, 375, 375, 375, 375,
+ 375, 375, 374, 374, 374, 420, 322, 323, 427, 439,
+ 439, 428, 428, 439, 339, 340, 431, 431, 439, 324,
+
+ 324, 324, 324, 338, 439, 485, 339, 340, 486, 487,
+ 439, 441, 443, 439, 442, 420, 322, 323, 450, 552,
+ 759, 513, 493, 494, 516, 553, 444, 339, 340, 429,
+ 338, 338, 439, 338, 338, 338, 338, 338, 338, 338,
+ 338, 338, 338, 338, 338, 338, 338, 338, 338, 338,
+ 338, 338, 338, 430, 430, 339, 340, 445, 338, 338,
+ 430, 430, 430, 430, 430, 430, 430, 430, 430, 430,
+ 430, 430, 430, 430, 430, 430, 430, 430, 430, 430,
+ 430, 430, 430, 430, 430, 338, 338, 338, 432, 432,
+ 432, 432, 758, 439, 339, 340, 432, 757, 339, 340,
+
+ 495, 417, 418, 432, 432, 432, 432, 432, 432, 360,
+ 360, 439, 438, 360, 360, 360, 360, 360, 360, 448,
+ 360, 360, 360, 360, 360, 360, 360, 360, 360, 360,
+ 360, 360, 439, 439, 360, 360, 360, 360, 439, 446,
+ 501, 501, 447, 504, 504, 416, 417, 418, 616, 617,
+ 339, 340, 638, 339, 340, 515, 439, 439, 449, 419,
+ 419, 419, 419, 514, 360, 360, 360, 375, 375, 580,
+ 375, 375, 375, 375, 375, 375, 375, 439, 375, 375,
+ 375, 375, 375, 375, 375, 375, 375, 375, 375, 375,
+ 517, 439, 375, 375, 375, 375, 495, 417, 418, 439,
+
+ 439, 511, 439, 512, 439, 439, 339, 340, 209, 439,
+ 419, 419, 419, 419, 439, 519, 520, 581, 518, 522,
+ 566, 566, 375, 375, 375, 500, 500, 573, 521, 578,
+ 339, 340, 500, 500, 500, 500, 500, 500, 500, 500,
+ 500, 500, 500, 500, 500, 500, 500, 500, 500, 500,
+ 500, 500, 500, 500, 500, 500, 500, 502, 502, 502,
+ 502, 532, 439, 439, 439, 502, 439, 339, 340, 439,
+ 339, 340, 502, 502, 502, 502, 502, 502, 505, 505,
+ 505, 505, 439, 533, 582, 576, 505, 574, 579, 534,
+ 575, 439, 439, 505, 505, 505, 505, 505, 505, 567,
+
+ 567, 567, 567, 590, 339, 340, 338, 567, 577, 583,
+ 439, 439, 625, 591, 567, 567, 567, 567, 567, 567,
+ 439, 439, 624, 439, 439, 439, 439, 439, 439, 439,
+ 616, 617, 439, 623, 616, 617, 685, 686, 685, 686,
+ 756, 628, 626, 632, 708, 755, 634, 685, 686, 302,
+ 302, 627, 629, 754, 753, 630, 631, 633, 752, 751,
+ 750, 709, 669, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 72, 72, 72, 72, 72,
+
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 76, 76, 76, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 76, 86, 86, 86,
+ 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
+ 86, 86, 86, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40, 40, 95,
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
+ 95, 95, 95, 95, 95, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 106, 106, 106, 106, 106, 106, 106, 106, 106,
+
+ 106, 106, 106, 106, 106, 106, 106, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 128, 128, 128, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 131,
+ 131, 131, 131, 131, 131, 131, 131, 131, 131, 131,
+ 131, 131, 131, 131, 131, 135, 135, 135, 135, 135,
+ 135, 135, 135, 135, 135, 135, 135, 135, 135, 135,
+ 135, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 184, 184, 184,
+ 184, 749, 748, 184, 184, 184, 190, 190, 190, 190,
+
+ 190, 190, 190, 190, 190, 190, 190, 190, 190, 190,
+ 190, 200, 200, 200, 200, 747, 746, 200, 200, 200,
+ 209, 745, 209, 209, 209, 209, 209, 209, 209, 209,
+ 209, 209, 209, 209, 209, 209, 212, 744, 212, 212,
+ 212, 212, 212, 212, 212, 212, 212, 212, 212, 212,
+ 212, 212, 216, 216, 216, 743, 742, 216, 216, 216,
+ 227, 741, 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 227, 227, 227, 229, 740, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
+ 229, 229, 230, 739, 230, 230, 230, 230, 230, 230,
+
+ 230, 230, 230, 230, 230, 230, 230, 230, 234, 234,
+ 234, 234, 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 234, 234, 236, 738, 236, 236, 737, 236,
+ 236, 236, 736, 735, 236, 236, 734, 733, 732, 236,
+ 238, 238, 238, 238, 731, 730, 238, 238, 238, 242,
+ 729, 242, 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 242, 246, 246, 246, 246, 728,
+ 727, 246, 246, 246, 251, 726, 251, 251, 251, 251,
+ 251, 251, 251, 251, 251, 251, 251, 251, 251, 251,
+ 254, 725, 254, 254, 254, 254, 254, 254, 254, 254,
+
+ 254, 724, 254, 254, 254, 254, 255, 723, 720, 719,
+ 255, 255, 255, 255, 718, 717, 255, 255, 257, 716,
+ 257, 257, 257, 257, 257, 257, 257, 257, 257, 257,
+ 257, 257, 257, 257, 261, 261, 261, 261, 715, 714,
+ 261, 261, 261, 264, 264, 264, 264, 264, 264, 264,
+ 264, 264, 264, 264, 264, 264, 264, 264, 264, 267,
+ 267, 267, 267, 713, 267, 267, 267, 267, 267, 267,
+ 267, 267, 267, 267, 267, 271, 712, 711, 271, 271,
+ 271, 271, 271, 271, 271, 710, 271, 271, 271, 271,
+ 271, 273, 707, 273, 273, 273, 273, 273, 273, 273,
+
+ 273, 273, 273, 273, 273, 273, 273, 274, 706, 274,
+ 274, 705, 274, 274, 274, 704, 703, 274, 274, 702,
+ 701, 700, 274, 279, 279, 279, 279, 699, 698, 279,
+ 279, 279, 284, 697, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 288, 288,
+ 696, 288, 288, 695, 694, 693, 288, 288, 315, 692,
+ 315, 315, 315, 315, 315, 315, 315, 315, 315, 315,
+ 315, 315, 315, 315, 319, 691, 319, 319, 319, 319,
+ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
+ 320, 690, 320, 320, 320, 320, 320, 320, 320, 320,
+
+ 320, 320, 320, 320, 320, 320, 328, 328, 689, 688,
+ 328, 328, 328, 329, 329, 687, 683, 329, 329, 329,
+ 330, 330, 330, 330, 330, 330, 330, 330, 330, 330,
+ 330, 330, 330, 330, 330, 330, 334, 334, 334, 334,
+ 334, 334, 334, 334, 334, 334, 334, 334, 334, 334,
+ 334, 334, 338, 682, 338, 338, 338, 338, 338, 338,
+ 338, 338, 338, 681, 338, 338, 338, 338, 209, 680,
+ 209, 209, 209, 209, 209, 209, 209, 209, 209, 209,
+ 209, 209, 209, 209, 345, 345, 679, 678, 677, 676,
+ 345, 346, 346, 346, 346, 675, 674, 346, 346, 346,
+
+ 346, 351, 673, 351, 351, 351, 351, 351, 351, 351,
+ 351, 351, 351, 351, 351, 351, 351, 227, 672, 227,
+ 227, 227, 227, 227, 227, 227, 227, 227, 227, 227,
+ 227, 227, 227, 229, 671, 229, 229, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 229, 229, 230,
+ 670, 230, 230, 230, 230, 230, 230, 230, 230, 230,
+ 230, 230, 230, 230, 230, 353, 668, 353, 353, 353,
+ 353, 353, 353, 353, 353, 353, 353, 353, 353, 353,
+ 353, 354, 667, 354, 354, 354, 354, 354, 354, 354,
+ 354, 354, 354, 354, 354, 354, 354, 234, 234, 234,
+
+ 234, 234, 234, 234, 234, 234, 234, 234, 234, 234,
+ 234, 234, 234, 236, 666, 236, 236, 665, 236, 236,
+ 236, 664, 663, 236, 236, 662, 661, 660, 236, 238,
+ 238, 238, 238, 659, 658, 238, 238, 238, 242, 657,
+ 242, 242, 242, 242, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 246, 246, 246, 246, 656, 655,
+ 246, 246, 246, 361, 361, 654, 653, 652, 361, 361,
+ 255, 651, 650, 649, 255, 255, 255, 255, 648, 647,
+ 255, 255, 257, 646, 257, 257, 257, 257, 257, 257,
+ 257, 257, 257, 257, 257, 257, 257, 257, 261, 261,
+
+ 261, 261, 645, 644, 261, 261, 261, 264, 264, 264,
+ 264, 264, 264, 264, 264, 264, 264, 264, 264, 264,
+ 264, 264, 264, 267, 267, 267, 267, 643, 267, 267,
+ 267, 267, 267, 267, 267, 267, 267, 267, 267, 271,
+ 642, 641, 271, 271, 271, 271, 271, 271, 271, 640,
+ 271, 271, 271, 271, 271, 274, 639, 274, 274, 638,
+ 274, 274, 274, 637, 636, 274, 274, 635, 622, 621,
+ 274, 279, 279, 279, 279, 620, 619, 279, 279, 279,
+ 284, 618, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 288, 288, 560, 288,
+
+ 288, 614, 613, 612, 288, 288, 315, 611, 315, 315,
+ 315, 315, 315, 315, 315, 315, 315, 315, 315, 315,
+ 315, 315, 319, 610, 319, 319, 319, 319, 319, 319,
+ 319, 319, 319, 319, 319, 319, 319, 319, 320, 609,
+ 320, 320, 320, 320, 320, 320, 320, 320, 320, 320,
+ 320, 320, 320, 320, 415, 415, 415, 415, 415, 415,
+ 415, 415, 415, 415, 415, 415, 415, 415, 415, 415,
+ 424, 424, 424, 424, 608, 607, 424, 424, 424, 425,
+ 425, 425, 425, 606, 605, 425, 425, 425, 330, 330,
+ 330, 330, 330, 330, 330, 330, 330, 330, 330, 330,
+
+ 330, 330, 330, 330, 334, 334, 334, 334, 334, 334,
+ 334, 334, 334, 334, 334, 334, 334, 334, 334, 334,
+ 338, 604, 338, 338, 338, 338, 338, 338, 338, 338,
+ 338, 603, 338, 338, 338, 338, 433, 433, 602, 601,
+ 600, 599, 433, 346, 346, 346, 346, 598, 597, 346,
+ 346, 346, 346, 351, 596, 351, 351, 351, 351, 351,
+ 351, 351, 351, 351, 351, 351, 351, 351, 351, 615,
+ 615, 615, 615, 615, 615, 615, 615, 615, 615, 615,
+ 615, 615, 615, 615, 615, 684, 684, 684, 684, 684,
+ 684, 684, 684, 684, 684, 684, 684, 684, 684, 684,
+
+ 684, 595, 594, 593, 592, 589, 588, 587, 586, 585,
+ 584, 571, 570, 569, 568, 565, 564, 563, 562, 561,
+ 560, 559, 558, 557, 556, 555, 554, 551, 550, 549,
+ 548, 547, 546, 545, 544, 543, 542, 541, 540, 539,
+ 538, 537, 536, 535, 531, 530, 529, 528, 527, 526,
+ 525, 524, 523, 510, 509, 508, 507, 506, 503, 499,
+ 498, 497, 496, 492, 491, 490, 489, 488, 484, 483,
+ 482, 479, 478, 477, 476, 475, 474, 473, 472, 471,
+ 470, 469, 468, 467, 466, 465, 464, 463, 462, 461,
+ 460, 459, 458, 457, 456, 455, 454, 453, 452, 451,
+
+ 439, 437, 436, 435, 434, 347, 426, 423, 422, 421,
+ 322, 414, 316, 408, 407, 406, 401, 400, 399, 398,
+ 397, 396, 395, 394, 391, 390, 389, 386, 385, 382,
+ 379, 378, 285, 282, 377, 376, 278, 373, 372, 243,
+ 356, 355, 235, 231, 352, 350, 349, 348, 218, 347,
+ 337, 206, 333, 202, 318, 186, 182, 316, 314, 313,
+ 312, 311, 310, 309, 305, 299, 298, 295, 294, 291,
+ 287, 286, 285, 283, 282, 281, 280, 260, 278, 277,
+ 272, 270, 263, 262, 260, 256, 250, 253, 250, 249,
+ 247, 245, 244, 243, 241, 240, 239, 237, 235, 228,
+
+ 231, 228, 226, 218, 208, 202, 199, 186, 182, 768,
+ 94, 94, 85, 77, 77, 39, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768
+ } ;
+
+static yyconst short int yy_chk[2775] =
+ { 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, 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, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
+ 765, 3, 5, 5, 3, 3, 3, 27, 27, 3,
+ 6, 6, 6, 28, 28, 3, 6, 11, 11, 11,
+ 3, 3, 12, 12, 12, 48, 17, 17, 66, 29,
+ 29, 60, 60, 18, 18, 310, 17, 29, 310, 17,
+ 17, 17, 17, 18, 61, 61, 18, 18, 18, 18,
+ 3, 3, 3, 4, 4, 4, 4, 109, 4, 4,
+ 21, 4, 4, 4, 511, 48, 4, 19, 19, 19,
+ 22, 109, 4, 20, 20, 20, 66, 4, 4, 141,
+
+ 4, 17, 61, 21, 21, 141, 21, 37, 18, 37,
+ 75, 19, 75, 22, 22, 511, 22, 20, 30, 30,
+ 37, 37, 37, 37, 128, 128, 30, 4, 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, 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, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 23, 23, 163, 24,
+ 24, 38, 764, 38, 41, 41, 41, 23, 130, 130,
+ 24, 45, 45, 45, 38, 38, 38, 38, 163, 23,
+ 23, 23, 24, 24, 24, 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, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 31, 31, 31, 32, 32, 32, 57,
+ 57, 57, 62, 62, 62, 62, 99, 99, 99, 99,
+ 131, 131, 133, 133, 167, 198, 198, 31, 167, 763,
+ 32, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 49, 53, 53,
+ 53, 121, 105, 105, 53, 158, 108, 108, 115, 115,
+ 160, 158, 63, 63, 63, 291, 168, 275, 63, 121,
+
+ 49, 188, 49, 275, 49, 63, 168, 205, 205, 160,
+ 49, 63, 291, 49, 49, 49, 405, 49, 49, 63,
+ 105, 170, 405, 49, 108, 49, 115, 180, 180, 180,
+ 180, 170, 762, 53, 170, 181, 181, 181, 185, 185,
+ 185, 188, 192, 192, 192, 209, 209, 63, 193, 193,
+ 193, 294, 194, 194, 194, 294, 192, 192, 192, 192,
+ 211, 211, 193, 193, 193, 193, 194, 194, 194, 194,
+ 195, 195, 195, 201, 201, 201, 210, 213, 213, 201,
+ 289, 257, 210, 210, 195, 195, 195, 195, 192, 217,
+ 217, 217, 264, 264, 289, 217, 194, 362, 211, 257,
+
+ 266, 266, 217, 248, 248, 248, 248, 761, 217, 267,
+ 267, 269, 269, 195, 298, 309, 217, 314, 298, 362,
+ 314, 321, 321, 321, 309, 330, 330, 760, 201, 314,
+ 314, 317, 317, 317, 317, 321, 321, 321, 321, 708,
+ 314, 332, 332, 708, 217, 241, 241, 241, 241, 241,
+ 241, 241, 241, 241, 241, 241, 241, 241, 241, 241,
+ 241, 241, 241, 241, 241, 241, 241, 241, 241, 241,
+ 241, 241, 241, 241, 241, 241, 241, 241, 241, 241,
+ 241, 241, 241, 241, 241, 241, 241, 241, 241, 241,
+ 241, 241, 241, 241, 241, 241, 241, 241, 241, 241,
+
+ 241, 241, 241, 250, 250, 250, 250, 334, 334, 250,
+ 336, 336, 338, 338, 250, 344, 344, 757, 250, 415,
+ 415, 250, 756, 250, 418, 418, 250, 278, 278, 755,
+ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
+ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
+ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
+ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
+ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
+ 278, 278, 278, 278, 278, 324, 324, 324, 339, 363,
+ 364, 340, 340, 365, 339, 339, 342, 342, 366, 324,
+
+ 324, 324, 324, 340, 371, 409, 342, 342, 409, 409,
+ 441, 363, 365, 444, 364, 420, 420, 420, 371, 485,
+ 754, 441, 494, 494, 444, 485, 366, 504, 504, 340,
+ 341, 341, 367, 341, 341, 341, 341, 341, 341, 341,
+ 341, 341, 341, 341, 341, 341, 341, 341, 341, 341,
+ 341, 341, 341, 341, 341, 341, 341, 367, 341, 341,
+ 341, 341, 341, 341, 341, 341, 341, 341, 341, 341,
+ 341, 341, 341, 341, 341, 341, 341, 341, 341, 341,
+ 341, 341, 341, 341, 341, 341, 341, 341, 343, 343,
+ 343, 343, 752, 369, 505, 505, 343, 750, 343, 343,
+
+ 495, 495, 495, 343, 343, 343, 343, 343, 343, 360,
+ 360, 368, 360, 360, 360, 360, 360, 360, 360, 369,
+ 360, 360, 360, 360, 360, 360, 360, 360, 360, 360,
+ 360, 360, 370, 442, 360, 360, 360, 360, 443, 368,
+ 428, 428, 368, 431, 431, 416, 416, 416, 561, 561,
+ 428, 428, 749, 431, 431, 443, 519, 445, 370, 416,
+ 416, 416, 416, 442, 360, 360, 360, 375, 375, 519,
+ 375, 375, 375, 375, 375, 375, 375, 440, 375, 375,
+ 375, 375, 375, 375, 375, 375, 375, 375, 375, 375,
+ 445, 447, 375, 375, 375, 375, 419, 419, 419, 446,
+
+ 450, 440, 449, 440, 520, 448, 503, 503, 503, 512,
+ 419, 419, 419, 419, 517, 447, 448, 520, 446, 450,
+ 501, 501, 375, 375, 375, 427, 427, 512, 449, 517,
+ 501, 501, 427, 427, 427, 427, 427, 427, 427, 427,
+ 427, 427, 427, 427, 427, 427, 427, 427, 427, 427,
+ 427, 427, 427, 427, 427, 427, 427, 429, 429, 429,
+ 429, 462, 514, 513, 518, 429, 515, 566, 566, 521,
+ 567, 567, 429, 429, 429, 429, 429, 429, 432, 432,
+ 432, 432, 516, 462, 521, 515, 432, 513, 518, 462,
+ 514, 574, 522, 432, 432, 432, 432, 432, 432, 502,
+
+ 502, 502, 502, 532, 565, 565, 565, 502, 516, 522,
+ 572, 573, 574, 532, 502, 502, 502, 502, 502, 502,
+ 575, 576, 573, 577, 578, 579, 580, 583, 581, 582,
+ 615, 615, 634, 572, 617, 617, 656, 656, 684, 684,
+ 748, 577, 575, 581, 681, 747, 583, 686, 686, 810,
+ 810, 576, 578, 745, 744, 579, 580, 582, 743, 742,
+ 740, 681, 634, 769, 769, 769, 769, 769, 769, 769,
+ 769, 769, 769, 769, 769, 769, 769, 769, 769, 770,
+ 770, 770, 770, 770, 770, 770, 770, 770, 770, 770,
+ 770, 770, 770, 770, 770, 771, 771, 771, 771, 771,
+
+ 771, 771, 771, 771, 771, 771, 771, 771, 771, 771,
+ 771, 772, 772, 772, 772, 772, 772, 772, 772, 772,
+ 772, 772, 772, 772, 772, 772, 772, 773, 773, 773,
+ 773, 773, 773, 773, 773, 773, 773, 773, 773, 773,
+ 773, 773, 773, 774, 774, 774, 774, 774, 774, 774,
+ 774, 774, 774, 774, 774, 774, 774, 774, 774, 775,
+ 775, 775, 775, 775, 775, 775, 775, 775, 775, 775,
+ 775, 775, 775, 775, 775, 776, 776, 776, 776, 776,
+ 776, 776, 776, 776, 776, 776, 776, 776, 776, 776,
+ 776, 777, 777, 777, 777, 777, 777, 777, 777, 777,
+
+ 777, 777, 777, 777, 777, 777, 777, 778, 778, 778,
+ 778, 778, 778, 778, 778, 778, 778, 778, 778, 778,
+ 778, 778, 778, 779, 779, 779, 779, 779, 779, 779,
+ 779, 779, 779, 779, 779, 779, 779, 779, 779, 780,
+ 780, 780, 780, 780, 780, 780, 780, 780, 780, 780,
+ 780, 780, 780, 780, 780, 781, 781, 781, 781, 781,
+ 781, 781, 781, 781, 781, 781, 781, 781, 781, 781,
+ 781, 782, 782, 782, 782, 782, 782, 782, 782, 782,
+ 782, 782, 782, 782, 782, 782, 782, 783, 783, 783,
+ 783, 739, 738, 783, 783, 783, 784, 784, 784, 784,
+
+ 784, 784, 784, 784, 784, 784, 784, 784, 784, 784,
+ 784, 785, 785, 785, 785, 737, 736, 785, 785, 785,
+ 786, 735, 786, 786, 786, 786, 786, 786, 786, 786,
+ 786, 786, 786, 786, 786, 786, 787, 734, 787, 787,
+ 787, 787, 787, 787, 787, 787, 787, 787, 787, 787,
+ 787, 787, 788, 788, 788, 733, 732, 788, 788, 788,
+ 789, 731, 789, 789, 789, 789, 789, 789, 789, 789,
+ 789, 789, 789, 789, 789, 789, 790, 729, 790, 790,
+ 790, 790, 790, 790, 790, 790, 790, 790, 790, 790,
+ 790, 790, 791, 727, 791, 791, 791, 791, 791, 791,
+
+ 791, 791, 791, 791, 791, 791, 791, 791, 792, 792,
+ 792, 792, 792, 792, 792, 792, 792, 792, 792, 792,
+ 792, 792, 792, 792, 793, 726, 793, 793, 725, 793,
+ 793, 793, 724, 723, 793, 793, 722, 721, 720, 793,
+ 794, 794, 794, 794, 719, 718, 794, 794, 794, 795,
+ 717, 795, 795, 795, 795, 795, 795, 795, 795, 795,
+ 795, 795, 795, 795, 795, 796, 796, 796, 796, 715,
+ 714, 796, 796, 796, 797, 713, 797, 797, 797, 797,
+ 797, 797, 797, 797, 797, 797, 797, 797, 797, 797,
+ 798, 712, 798, 798, 798, 798, 798, 798, 798, 798,
+
+ 798, 710, 798, 798, 798, 798, 799, 709, 707, 706,
+ 799, 799, 799, 799, 705, 704, 799, 799, 800, 703,
+ 800, 800, 800, 800, 800, 800, 800, 800, 800, 800,
+ 800, 800, 800, 800, 801, 801, 801, 801, 702, 701,
+ 801, 801, 801, 802, 802, 802, 802, 802, 802, 802,
+ 802, 802, 802, 802, 802, 802, 802, 802, 802, 803,
+ 803, 803, 803, 700, 803, 803, 803, 803, 803, 803,
+ 803, 803, 803, 803, 803, 804, 699, 698, 804, 804,
+ 804, 804, 804, 804, 804, 682, 804, 804, 804, 804,
+ 804, 805, 680, 805, 805, 805, 805, 805, 805, 805,
+
+ 805, 805, 805, 805, 805, 805, 805, 806, 679, 806,
+ 806, 678, 806, 806, 806, 677, 675, 806, 806, 674,
+ 672, 671, 806, 807, 807, 807, 807, 670, 669, 807,
+ 807, 807, 808, 668, 808, 808, 808, 808, 808, 808,
+ 808, 808, 808, 808, 808, 808, 808, 808, 809, 809,
+ 667, 809, 809, 666, 665, 664, 809, 809, 811, 663,
+ 811, 811, 811, 811, 811, 811, 811, 811, 811, 811,
+ 811, 811, 811, 811, 812, 662, 812, 812, 812, 812,
+ 812, 812, 812, 812, 812, 812, 812, 812, 812, 812,
+ 813, 661, 813, 813, 813, 813, 813, 813, 813, 813,
+
+ 813, 813, 813, 813, 813, 813, 814, 814, 660, 659,
+ 814, 814, 814, 815, 815, 658, 655, 815, 815, 815,
+ 816, 816, 816, 816, 816, 816, 816, 816, 816, 816,
+ 816, 816, 816, 816, 816, 816, 817, 817, 817, 817,
+ 817, 817, 817, 817, 817, 817, 817, 817, 817, 817,
+ 817, 817, 818, 653, 818, 818, 818, 818, 818, 818,
+ 818, 818, 818, 652, 818, 818, 818, 818, 819, 651,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 820, 820, 650, 646, 644, 643,
+ 820, 821, 821, 821, 821, 642, 641, 821, 821, 821,
+
+ 821, 822, 639, 822, 822, 822, 822, 822, 822, 822,
+ 822, 822, 822, 822, 822, 822, 822, 823, 637, 823,
+ 823, 823, 823, 823, 823, 823, 823, 823, 823, 823,
+ 823, 823, 823, 824, 636, 824, 824, 824, 824, 824,
+ 824, 824, 824, 824, 824, 824, 824, 824, 824, 825,
+ 635, 825, 825, 825, 825, 825, 825, 825, 825, 825,
+ 825, 825, 825, 825, 825, 826, 633, 826, 826, 826,
+ 826, 826, 826, 826, 826, 826, 826, 826, 826, 826,
+ 826, 827, 632, 827, 827, 827, 827, 827, 827, 827,
+ 827, 827, 827, 827, 827, 827, 827, 828, 828, 828,
+
+ 828, 828, 828, 828, 828, 828, 828, 828, 828, 828,
+ 828, 828, 828, 829, 631, 829, 829, 630, 829, 829,
+ 829, 629, 628, 829, 829, 627, 626, 625, 829, 830,
+ 830, 830, 830, 624, 623, 830, 830, 830, 831, 621,
+ 831, 831, 831, 831, 831, 831, 831, 831, 831, 831,
+ 831, 831, 831, 831, 832, 832, 832, 832, 619, 612,
+ 832, 832, 832, 833, 833, 611, 610, 609, 833, 833,
+ 834, 608, 607, 606, 834, 834, 834, 834, 604, 601,
+ 834, 834, 835, 600, 835, 835, 835, 835, 835, 835,
+ 835, 835, 835, 835, 835, 835, 835, 835, 836, 836,
+
+ 836, 836, 599, 598, 836, 836, 836, 837, 837, 837,
+ 837, 837, 837, 837, 837, 837, 837, 837, 837, 837,
+ 837, 837, 837, 838, 838, 838, 838, 597, 838, 838,
+ 838, 838, 838, 838, 838, 838, 838, 838, 838, 839,
+ 596, 595, 839, 839, 839, 839, 839, 839, 839, 594,
+ 839, 839, 839, 839, 839, 840, 593, 840, 840, 592,
+ 840, 840, 840, 591, 590, 840, 840, 588, 569, 568,
+ 840, 841, 841, 841, 841, 564, 563, 841, 841, 841,
+ 842, 562, 842, 842, 842, 842, 842, 842, 842, 842,
+ 842, 842, 842, 842, 842, 842, 843, 843, 560, 843,
+
+ 843, 559, 558, 557, 843, 843, 844, 556, 844, 844,
+ 844, 844, 844, 844, 844, 844, 844, 844, 844, 844,
+ 844, 844, 845, 555, 845, 845, 845, 845, 845, 845,
+ 845, 845, 845, 845, 845, 845, 845, 845, 846, 554,
+ 846, 846, 846, 846, 846, 846, 846, 846, 846, 846,
+ 846, 846, 846, 846, 847, 847, 847, 847, 847, 847,
+ 847, 847, 847, 847, 847, 847, 847, 847, 847, 847,
+ 848, 848, 848, 848, 553, 552, 848, 848, 848, 849,
+ 849, 849, 849, 551, 549, 849, 849, 849, 850, 850,
+ 850, 850, 850, 850, 850, 850, 850, 850, 850, 850,
+
+ 850, 850, 850, 850, 851, 851, 851, 851, 851, 851,
+ 851, 851, 851, 851, 851, 851, 851, 851, 851, 851,
+ 852, 548, 852, 852, 852, 852, 852, 852, 852, 852,
+ 852, 546, 852, 852, 852, 852, 853, 853, 545, 544,
+ 543, 542, 853, 854, 854, 854, 854, 541, 540, 854,
+ 854, 854, 854, 855, 539, 855, 855, 855, 855, 855,
+ 855, 855, 855, 855, 855, 855, 855, 855, 855, 856,
+ 856, 856, 856, 856, 856, 856, 856, 856, 856, 856,
+ 856, 856, 856, 856, 856, 857, 857, 857, 857, 857,
+ 857, 857, 857, 857, 857, 857, 857, 857, 857, 857,
+
+ 857, 538, 536, 534, 533, 530, 528, 526, 525, 524,
+ 523, 509, 508, 507, 506, 500, 499, 498, 497, 496,
+ 492, 491, 490, 489, 488, 487, 486, 483, 482, 481,
+ 480, 479, 478, 476, 475, 474, 473, 472, 471, 469,
+ 468, 467, 464, 463, 461, 460, 459, 458, 457, 454,
+ 453, 452, 451, 439, 437, 436, 435, 434, 430, 426,
+ 423, 422, 421, 414, 413, 412, 411, 410, 408, 407,
+ 406, 404, 403, 402, 401, 400, 399, 398, 397, 396,
+ 395, 394, 393, 392, 391, 390, 388, 387, 386, 384,
+ 383, 382, 381, 380, 379, 378, 377, 376, 373, 372,
+
+ 361, 356, 355, 352, 350, 346, 337, 327, 326, 325,
+ 323, 318, 315, 313, 312, 311, 308, 307, 306, 305,
+ 303, 301, 300, 299, 297, 296, 295, 293, 292, 290,
+ 287, 286, 284, 282, 281, 280, 276, 263, 262, 243,
+ 240, 239, 234, 231, 226, 222, 221, 220, 219, 216,
+ 208, 207, 204, 203, 189, 187, 183, 179, 176, 175,
+ 174, 173, 172, 171, 169, 166, 165, 162, 161, 159,
+ 156, 155, 154, 153, 151, 149, 148, 146, 144, 143,
+ 137, 134, 125, 124, 122, 119, 114, 112, 107, 103,
+ 97, 92, 89, 87, 85, 84, 83, 80, 76, 74,
+
+ 73, 71, 67, 65, 59, 55, 50, 47, 43, 39,
+ 16, 15, 10, 8, 7, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
+ 768, 768, 768, 768
+ } ;
+
+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
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "scan.l"
+#define INITIAL 0
+/* 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.3 1996/06/19 20:47:13 nate Exp $ */
+
+#include "flexdef.h"
+#include "parse.h"
+
+#define ACTION_ECHO add_action( yytext )
+#define ACTION_IFDEF(def, should_define) \
+ { \
+ if ( should_define ) \
+ action_define( def, 1 ); \
+ }
+
+#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;
+#define YY_STACK_USED 1
+#define YY_NO_TOP_STATE 1
+#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 COMMENT 14
+#define ACTION_STRING 15
+#define PERCENT_BRACE_ACTION 16
+
+#define OPTION 17
+#define LINEDIR 18
+
+#line 1333 "scan.c"
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YY_PROTO(( yyconst char * ));
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#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 = '*', n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ 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
+
+#define YY_RULE_SETUP \
+ if ( yyleng > 0 ) \
+ yy_current_buffer->yy_at_bol = \
+ (yytext[yyleng - 1] == '\n'); \
+ YY_USER_ACTION
+
+YY_DECL
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 94 "scan.l"
+
+ static int bracelevel, didadef, indented_code;
+ static int doing_rule_action = false;
+ static int option_sense;
+
+ int doing_codeblock = false;
+ int i;
+ Char nmdef[MAXLINE], myesc();
+
+
+#line 1498 "scan.c"
+
+ if ( yy_init )
+ {
+ yy_init = 0;
+
+#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_current_buffer =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_load_buffer_state();
+ }
+
+ 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;
+ yy_current_state += YY_AT_BOL();
+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 >= 769 )
+ 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] != 2716 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = yy_last_accepting_cpos;
+ yy_current_state = yy_last_accepting_state;
+ 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_RULE_SETUP
+#line 105 "scan.l"
+indented_code = true; BEGIN(CODEBLOCK);
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 106 "scan.l"
+ACTION_ECHO; yy_push_state( COMMENT );
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 107 "scan.l"
+yy_push_state( LINEDIR );
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 108 "scan.l"
+return SCDECL;
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 109 "scan.l"
+return XSCDECL;
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 110 "scan.l"
+{
+ ++linenum;
+ line_directive_out( (FILE *) 0, 1 );
+ indented_code = false;
+ BEGIN(CODEBLOCK);
+ }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 117 "scan.l"
+/* discard */
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 119 "scan.l"
+{
+ sectnum = 2;
+ bracelevel = 0;
+ mark_defs1();
+ line_directive_out( (FILE *) 0, 1 );
+ BEGIN(SECT2PROLOG);
+ return SECTEND;
+ }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 128 "scan.l"
+yytext_is_array = false; ++linenum;
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 129 "scan.l"
+yytext_is_array = true; ++linenum;
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 131 "scan.l"
+BEGIN(OPTION); return OPTION_OP;
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 133 "scan.l"
+++linenum; /* ignore */
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 134 "scan.l"
+++linenum; /* ignore */
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 136 "scan.l"
+synerr( _( "unrecognized '%' directive" ) );
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 138 "scan.l"
+{
+ strcpy( nmstr, yytext );
+ didadef = false;
+ BEGIN(PICKUPDEF);
+ }
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 144 "scan.l"
+RETURNNAME;
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 145 "scan.l"
+++linenum; /* allows blank lines in section 1 */
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 146 "scan.l"
+ACTION_ECHO; ++linenum; /* maybe end of comment line */
+ YY_BREAK
+
+
+case 19:
+YY_RULE_SETUP
+#line 151 "scan.l"
+ACTION_ECHO; yy_pop_state();
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 152 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 153 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 154 "scan.l"
+++linenum; ACTION_ECHO;
+ YY_BREAK
+
+
+case 23:
+YY_RULE_SETUP
+#line 158 "scan.l"
+yy_pop_state();
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 159 "scan.l"
+linenum = myctoi( yytext );
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 161 "scan.l"
+{
+ flex_free( (void *) infilename );
+ infilename = copy_string( yytext + 1 );
+ infilename[strlen( infilename ) - 1] = '\0';
+ }
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 166 "scan.l"
+/* ignore spurious characters */
+ YY_BREAK
+
+
+case 27:
+YY_RULE_SETUP
+#line 170 "scan.l"
+++linenum; BEGIN(INITIAL);
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 172 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 174 "scan.l"
+{
+ ++linenum;
+ ACTION_ECHO;
+ if ( indented_code )
+ BEGIN(INITIAL);
+ }
+ YY_BREAK
+
+
+case 30:
+YY_RULE_SETUP
+#line 184 "scan.l"
+/* separates name and definition */
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 186 "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 32:
+YY_RULE_SETUP
+#line 201 "scan.l"
+{
+ if ( ! didadef )
+ synerr( _( "incomplete name definition" ) );
+ BEGIN(INITIAL);
+ ++linenum;
+ }
+ YY_BREAK
+
+
+case 33:
+YY_RULE_SETUP
+#line 211 "scan.l"
+++linenum; BEGIN(INITIAL);
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 212 "scan.l"
+option_sense = true;
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 214 "scan.l"
+return '=';
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 216 "scan.l"
+option_sense = ! option_sense;
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 218 "scan.l"
+csize = option_sense ? 128 : 256;
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 219 "scan.l"
+csize = option_sense ? 256 : 128;
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 221 "scan.l"
+long_align = option_sense;
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 222 "scan.l"
+{
+ action_define( "YY_ALWAYS_INTERACTIVE", option_sense );
+ }
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 225 "scan.l"
+yytext_is_array = option_sense;
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 226 "scan.l"
+backing_up_report = option_sense;
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 227 "scan.l"
+interactive = ! option_sense;
+ YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 228 "scan.l"
+C_plus_plus = option_sense;
+ YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 229 "scan.l"
+caseins = ! option_sense;
+ YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 230 "scan.l"
+caseins = option_sense;
+ YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 231 "scan.l"
+ddebug = option_sense;
+ YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 232 "scan.l"
+spprdflt = ! option_sense;
+ YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 233 "scan.l"
+useecs = option_sense;
+ YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 234 "scan.l"
+{
+ useecs = usemecs = false;
+ use_read = fullspd = true;
+ }
+ YY_BREAK
+case 51:
+YY_RULE_SETUP
+#line 238 "scan.l"
+{
+ useecs = usemecs = false;
+ use_read = fulltbl = true;
+ }
+ YY_BREAK
+case 52:
+YY_RULE_SETUP
+#line 242 "scan.l"
+ACTION_IFDEF("YY_NO_INPUT", ! option_sense);
+ YY_BREAK
+case 53:
+YY_RULE_SETUP
+#line 243 "scan.l"
+interactive = option_sense;
+ YY_BREAK
+case 54:
+YY_RULE_SETUP
+#line 244 "scan.l"
+lex_compat = option_sense;
+ YY_BREAK
+case 55:
+YY_RULE_SETUP
+#line 245 "scan.l"
+{
+ action_define( "YY_MAIN", option_sense );
+ do_yywrap = ! option_sense;
+ }
+ YY_BREAK
+case 56:
+YY_RULE_SETUP
+#line 249 "scan.l"
+usemecs = option_sense;
+ YY_BREAK
+case 57:
+YY_RULE_SETUP
+#line 250 "scan.l"
+{
+ action_define( "YY_NEVER_INTERACTIVE", option_sense );
+ }
+ YY_BREAK
+case 58:
+YY_RULE_SETUP
+#line 253 "scan.l"
+performance_report += option_sense ? 1 : -1;
+ YY_BREAK
+case 59:
+YY_RULE_SETUP
+#line 254 "scan.l"
+yytext_is_array = ! option_sense;
+ YY_BREAK
+case 60:
+YY_RULE_SETUP
+#line 255 "scan.l"
+use_read = option_sense;
+ YY_BREAK
+case 61:
+YY_RULE_SETUP
+#line 256 "scan.l"
+reject_really_used = option_sense;
+ YY_BREAK
+case 62:
+YY_RULE_SETUP
+#line 257 "scan.l"
+action_define( "YY_STACK_USED", option_sense );
+ YY_BREAK
+case 63:
+YY_RULE_SETUP
+#line 258 "scan.l"
+do_stdinit = option_sense;
+ YY_BREAK
+case 64:
+YY_RULE_SETUP
+#line 259 "scan.l"
+use_stdout = option_sense;
+ YY_BREAK
+case 65:
+YY_RULE_SETUP
+#line 260 "scan.l"
+ACTION_IFDEF("YY_NO_UNPUT", ! option_sense);
+ YY_BREAK
+case 66:
+YY_RULE_SETUP
+#line 261 "scan.l"
+printstats = option_sense;
+ YY_BREAK
+case 67:
+YY_RULE_SETUP
+#line 262 "scan.l"
+nowarn = ! option_sense;
+ YY_BREAK
+case 68:
+YY_RULE_SETUP
+#line 263 "scan.l"
+do_yylineno = option_sense;
+ YY_BREAK
+case 69:
+YY_RULE_SETUP
+#line 264 "scan.l"
+yymore_really_used = option_sense;
+ YY_BREAK
+case 70:
+YY_RULE_SETUP
+#line 265 "scan.l"
+do_yywrap = option_sense;
+ YY_BREAK
+case 71:
+YY_RULE_SETUP
+#line 267 "scan.l"
+ACTION_IFDEF("YY_NO_PUSH_STATE", ! option_sense);
+ YY_BREAK
+case 72:
+YY_RULE_SETUP
+#line 268 "scan.l"
+ACTION_IFDEF("YY_NO_POP_STATE", ! option_sense);
+ YY_BREAK
+case 73:
+YY_RULE_SETUP
+#line 269 "scan.l"
+ACTION_IFDEF("YY_NO_TOP_STATE", ! option_sense);
+ YY_BREAK
+case 74:
+YY_RULE_SETUP
+#line 271 "scan.l"
+ACTION_IFDEF("YY_NO_SCAN_BUFFER", ! option_sense);
+ YY_BREAK
+case 75:
+YY_RULE_SETUP
+#line 272 "scan.l"
+ACTION_IFDEF("YY_NO_SCAN_BYTES", ! option_sense);
+ YY_BREAK
+case 76:
+YY_RULE_SETUP
+#line 273 "scan.l"
+ACTION_IFDEF("YY_NO_SCAN_STRING", ! option_sense);
+ YY_BREAK
+case 77:
+YY_RULE_SETUP
+#line 275 "scan.l"
+return OPT_OUTFILE;
+ YY_BREAK
+case 78:
+YY_RULE_SETUP
+#line 276 "scan.l"
+return OPT_PREFIX;
+ YY_BREAK
+case 79:
+YY_RULE_SETUP
+#line 277 "scan.l"
+return OPT_YYCLASS;
+ YY_BREAK
+case 80:
+YY_RULE_SETUP
+#line 279 "scan.l"
+{
+ strcpy( nmstr, yytext + 1 );
+ nmstr[strlen( nmstr ) - 1] = '\0';
+ return NAME;
+ }
+ YY_BREAK
+case 81:
+YY_RULE_SETUP
+#line 285 "scan.l"
+{
+ format_synerr( _( "unrecognized %%option: %s" ),
+ yytext );
+ BEGIN(RECOVER);
+ }
+ YY_BREAK
+
+case 82:
+YY_RULE_SETUP
+#line 292 "scan.l"
+++linenum; BEGIN(INITIAL);
+ YY_BREAK
+
+case 83:
+YY_RULE_SETUP
+#line 296 "scan.l"
+++bracelevel; yyless( 2 ); /* eat only %{ */
+ YY_BREAK
+case 84:
+YY_RULE_SETUP
+#line 297 "scan.l"
+--bracelevel; yyless( 2 ); /* eat only %} */
+ YY_BREAK
+case 85:
+YY_RULE_SETUP
+#line 299 "scan.l"
+ACTION_ECHO; /* indented code in prolog */
+ YY_BREAK
+case 86:
+YY_RULE_SETUP
+#line 301 "scan.l"
+{ /* non-indented code */
+ if ( bracelevel <= 0 )
+ { /* not in %{ ... %} */
+ yyless( 0 ); /* put it all back */
+ yy_set_bol( 1 );
+ mark_prolog();
+ BEGIN(SECT2);
+ }
+ else
+ ACTION_ECHO;
+ }
+ YY_BREAK
+case 87:
+YY_RULE_SETUP
+#line 313 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 88:
+YY_RULE_SETUP
+#line 314 "scan.l"
+++linenum; ACTION_ECHO;
+ YY_BREAK
+case YY_STATE_EOF(SECT2PROLOG):
+#line 316 "scan.l"
+{
+ mark_prolog();
+ sectnum = 0;
+ yyterminate(); /* to stop the parser */
+ }
+ YY_BREAK
+
+
+case 89:
+YY_RULE_SETUP
+#line 324 "scan.l"
+++linenum; /* allow blank lines in section 2 */
+ YY_BREAK
+case 90:
+YY_RULE_SETUP
+#line 326 "scan.l"
+{
+ indented_code = false;
+ doing_codeblock = true;
+ bracelevel = 1;
+ BEGIN(PERCENT_BRACE_ACTION);
+ }
+ YY_BREAK
+case 91:
+YY_RULE_SETUP
+#line 333 "scan.l"
+BEGIN(SC); return '<';
+ YY_BREAK
+case 92:
+YY_RULE_SETUP
+#line 334 "scan.l"
+return '^';
+ YY_BREAK
+case 93:
+YY_RULE_SETUP
+#line 335 "scan.l"
+BEGIN(QUOTE); return '"';
+ YY_BREAK
+case 94:
+*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_RULE_SETUP
+#line 336 "scan.l"
+BEGIN(NUM); return '{';
+ YY_BREAK
+case 95:
+*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_RULE_SETUP
+#line 337 "scan.l"
+return '$';
+ YY_BREAK
+case 96:
+YY_RULE_SETUP
+#line 339 "scan.l"
+{
+ bracelevel = 1;
+ BEGIN(PERCENT_BRACE_ACTION);
+
+ if ( in_rule )
+ {
+ doing_rule_action = true;
+ in_rule = false;
+ return '\n';
+ }
+ }
+ YY_BREAK
+case 97:
+YY_RULE_SETUP
+#line 350 "scan.l"
+continued_action = true; ++linenum; return '\n';
+ YY_BREAK
+case 98:
+YY_RULE_SETUP
+#line 352 "scan.l"
+{
+ yyless( yyleng - 2 ); /* put back '/', '*' */
+ bracelevel = 0;
+ continued_action = false;
+ BEGIN(ACTION);
+ }
+ YY_BREAK
+case 99:
+YY_RULE_SETUP
+#line 359 "scan.l"
+/* allow indented rules */
+ YY_BREAK
+case 100:
+YY_RULE_SETUP
+#line 361 "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);
+
+ if ( in_rule )
+ {
+ doing_rule_action = true;
+ in_rule = false;
+ return '\n';
+ }
+ }
+ YY_BREAK
+case 101:
+YY_RULE_SETUP
+#line 378 "scan.l"
+{
+ bracelevel = 0;
+ continued_action = false;
+ BEGIN(ACTION);
+ unput( '\n' ); /* so <ACTION> sees it */
+
+ if ( in_rule )
+ {
+ doing_rule_action = true;
+ in_rule = false;
+ return '\n';
+ }
+ }
+ YY_BREAK
+case 102:
+#line 393 "scan.l"
+case 103:
+YY_RULE_SETUP
+#line 393 "scan.l"
+return EOF_OP;
+ YY_BREAK
+case 104:
+YY_RULE_SETUP
+#line 395 "scan.l"
+{
+ sectnum = 3;
+ BEGIN(SECT3);
+ yyterminate(); /* to stop the parser */
+ }
+ YY_BREAK
+case 105:
+YY_RULE_SETUP
+#line 401 "scan.l"
+{
+ int cclval;
+
+ strcpy( nmstr, yytext );
+
+ /* Check to see if we've already encountered this
+ * ccl.
+ */
+ if ( (cclval = ccllookup( (Char *) nmstr )) != 0 )
+ {
+ 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 106:
+YY_RULE_SETUP
+#line 435 "scan.l"
+{
+ register Char *nmdefptr;
+ Char *ndlookup();
+
+ strcpy( nmstr, yytext + 1 );
+ nmstr[yyleng - 2] = '\0'; /* chop trailing brace */
+
+ if ( (nmdefptr = ndlookup( nmstr )) == 0 )
+ 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 107:
+YY_RULE_SETUP
+#line 469 "scan.l"
+return (unsigned char) yytext[0];
+ YY_BREAK
+case 108:
+YY_RULE_SETUP
+#line 470 "scan.l"
+RETURNCHAR;
+ YY_BREAK
+
+
+case 109:
+YY_RULE_SETUP
+#line 475 "scan.l"
+return (unsigned char) yytext[0];
+ YY_BREAK
+case 110:
+YY_RULE_SETUP
+#line 476 "scan.l"
+BEGIN(SECT2); return '>';
+ YY_BREAK
+case 111:
+*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_RULE_SETUP
+#line 477 "scan.l"
+BEGIN(CARETISBOL); return '>';
+ YY_BREAK
+case 112:
+YY_RULE_SETUP
+#line 478 "scan.l"
+RETURNNAME;
+ YY_BREAK
+case 113:
+YY_RULE_SETUP
+#line 479 "scan.l"
+{
+ format_synerr( _( "bad <start condition>: %s" ),
+ yytext );
+ }
+ YY_BREAK
+
+case 114:
+YY_RULE_SETUP
+#line 485 "scan.l"
+BEGIN(SECT2); return '^';
+ YY_BREAK
+
+case 115:
+YY_RULE_SETUP
+#line 489 "scan.l"
+RETURNCHAR;
+ YY_BREAK
+case 116:
+YY_RULE_SETUP
+#line 490 "scan.l"
+BEGIN(SECT2); return '"';
+ YY_BREAK
+case 117:
+YY_RULE_SETUP
+#line 492 "scan.l"
+{
+ synerr( _( "missing quote" ) );
+ BEGIN(SECT2);
+ ++linenum;
+ return '"';
+ }
+ YY_BREAK
+
+
+case 118:
+*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_RULE_SETUP
+#line 502 "scan.l"
+BEGIN(CCL); return '^';
+ YY_BREAK
+case 119:
+*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_RULE_SETUP
+#line 503 "scan.l"
+return '^';
+ YY_BREAK
+case 120:
+YY_RULE_SETUP
+#line 504 "scan.l"
+BEGIN(CCL); RETURNCHAR;
+ YY_BREAK
+
+
+case 121:
+*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_RULE_SETUP
+#line 508 "scan.l"
+return '-';
+ YY_BREAK
+case 122:
+YY_RULE_SETUP
+#line 509 "scan.l"
+RETURNCHAR;
+ YY_BREAK
+case 123:
+YY_RULE_SETUP
+#line 510 "scan.l"
+BEGIN(SECT2); return ']';
+ YY_BREAK
+case 124:
+YY_RULE_SETUP
+#line 511 "scan.l"
+{
+ synerr( _( "bad character class" ) );
+ BEGIN(SECT2);
+ return ']';
+ }
+ YY_BREAK
+
+
+case 125:
+YY_RULE_SETUP
+#line 519 "scan.l"
+BEGIN(CCL); return CCE_ALNUM;
+ YY_BREAK
+case 126:
+YY_RULE_SETUP
+#line 520 "scan.l"
+BEGIN(CCL); return CCE_ALPHA;
+ YY_BREAK
+case 127:
+YY_RULE_SETUP
+#line 521 "scan.l"
+BEGIN(CCL); return CCE_BLANK;
+ YY_BREAK
+case 128:
+YY_RULE_SETUP
+#line 522 "scan.l"
+BEGIN(CCL); return CCE_CNTRL;
+ YY_BREAK
+case 129:
+YY_RULE_SETUP
+#line 523 "scan.l"
+BEGIN(CCL); return CCE_DIGIT;
+ YY_BREAK
+case 130:
+YY_RULE_SETUP
+#line 524 "scan.l"
+BEGIN(CCL); return CCE_GRAPH;
+ YY_BREAK
+case 131:
+YY_RULE_SETUP
+#line 525 "scan.l"
+BEGIN(CCL); return CCE_LOWER;
+ YY_BREAK
+case 132:
+YY_RULE_SETUP
+#line 526 "scan.l"
+BEGIN(CCL); return CCE_PRINT;
+ YY_BREAK
+case 133:
+YY_RULE_SETUP
+#line 527 "scan.l"
+BEGIN(CCL); return CCE_PUNCT;
+ YY_BREAK
+case 134:
+YY_RULE_SETUP
+#line 528 "scan.l"
+BEGIN(CCL); return CCE_SPACE;
+ YY_BREAK
+case 135:
+YY_RULE_SETUP
+#line 529 "scan.l"
+BEGIN(CCL); return CCE_UPPER;
+ YY_BREAK
+case 136:
+YY_RULE_SETUP
+#line 530 "scan.l"
+BEGIN(CCL); return CCE_XDIGIT;
+ YY_BREAK
+case 137:
+YY_RULE_SETUP
+#line 531 "scan.l"
+{
+ format_synerr(
+ _( "bad character class expression: %s" ),
+ yytext );
+ BEGIN(CCL); return CCE_ALNUM;
+ }
+ YY_BREAK
+
+
+case 138:
+YY_RULE_SETUP
+#line 540 "scan.l"
+{
+ yylval = myctoi( yytext );
+ return NUMBER;
+ }
+ YY_BREAK
+case 139:
+YY_RULE_SETUP
+#line 545 "scan.l"
+return ',';
+ YY_BREAK
+case 140:
+YY_RULE_SETUP
+#line 546 "scan.l"
+BEGIN(SECT2); return '}';
+ YY_BREAK
+case 141:
+YY_RULE_SETUP
+#line 548 "scan.l"
+{
+ synerr( _( "bad character inside {}'s" ) );
+ BEGIN(SECT2);
+ return '}';
+ }
+ YY_BREAK
+case 142:
+YY_RULE_SETUP
+#line 554 "scan.l"
+{
+ synerr( _( "missing }" ) );
+ BEGIN(SECT2);
+ ++linenum;
+ return '}';
+ }
+ YY_BREAK
+
+
+case 143:
+YY_RULE_SETUP
+#line 564 "scan.l"
+bracelevel = 0;
+ YY_BREAK
+case 144:
+YY_RULE_SETUP
+#line 566 "scan.l"
+ACTION_ECHO; yy_push_state( COMMENT );
+ YY_BREAK
+
+case 145:
+YY_RULE_SETUP
+#line 569 "scan.l"
+{
+ ACTION_ECHO;
+ CHECK_REJECT(yytext);
+ }
+ YY_BREAK
+case 146:
+YY_RULE_SETUP
+#line 573 "scan.l"
+{
+ ACTION_ECHO;
+ CHECK_YYMORE(yytext);
+ }
+ YY_BREAK
+
+case 147:
+YY_RULE_SETUP
+#line 579 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 148:
+YY_RULE_SETUP
+#line 580 "scan.l"
+{
+ ++linenum;
+ ACTION_ECHO;
+ if ( bracelevel == 0 ||
+ (doing_codeblock && indented_code) )
+ {
+ if ( doing_rule_action )
+ add_action( "\tYY_BREAK\n" );
+
+ doing_rule_action = doing_codeblock = false;
+ BEGIN(SECT2);
+ }
+ }
+ YY_BREAK
+
+/* Reject and YYmore() are checked for above, in PERCENT_BRACE_ACTION */
+
+case 149:
+YY_RULE_SETUP
+#line 598 "scan.l"
+ACTION_ECHO; ++bracelevel;
+ YY_BREAK
+case 150:
+YY_RULE_SETUP
+#line 599 "scan.l"
+ACTION_ECHO; --bracelevel;
+ YY_BREAK
+case 151:
+YY_RULE_SETUP
+#line 600 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 152:
+YY_RULE_SETUP
+#line 601 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 153:
+YY_RULE_SETUP
+#line 602 "scan.l"
+ACTION_ECHO; /* character constant */
+ YY_BREAK
+case 154:
+YY_RULE_SETUP
+#line 603 "scan.l"
+ACTION_ECHO; BEGIN(ACTION_STRING);
+ YY_BREAK
+case 155:
+YY_RULE_SETUP
+#line 604 "scan.l"
+{
+ ++linenum;
+ ACTION_ECHO;
+ if ( bracelevel == 0 )
+ {
+ if ( doing_rule_action )
+ add_action( "\tYY_BREAK\n" );
+
+ doing_rule_action = false;
+ BEGIN(SECT2);
+ }
+ }
+ YY_BREAK
+case 156:
+YY_RULE_SETUP
+#line 616 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+
+
+case 157:
+YY_RULE_SETUP
+#line 620 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 158:
+YY_RULE_SETUP
+#line 621 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 159:
+YY_RULE_SETUP
+#line 622 "scan.l"
+++linenum; ACTION_ECHO;
+ YY_BREAK
+case 160:
+YY_RULE_SETUP
+#line 623 "scan.l"
+ACTION_ECHO; BEGIN(ACTION);
+ YY_BREAK
+case 161:
+YY_RULE_SETUP
+#line 624 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+
+case YY_STATE_EOF(COMMENT):
+case YY_STATE_EOF(ACTION):
+case YY_STATE_EOF(ACTION_STRING):
+#line 627 "scan.l"
+{
+ synerr( _( "EOF encountered inside an action" ) );
+ yyterminate();
+ }
+ YY_BREAK
+case 162:
+YY_RULE_SETUP
+#line 633 "scan.l"
+{
+ yylval = myesc( (Char *) yytext );
+
+ if ( YY_START == FIRSTCCL )
+ BEGIN(CCL);
+
+ return CHAR;
+ }
+ YY_BREAK
+
+case 163:
+YY_RULE_SETUP
+#line 644 "scan.l"
+ECHO;
+ YY_BREAK
+case YY_STATE_EOF(SECT3):
+#line 645 "scan.l"
+sectnum = 0; yyterminate();
+ YY_BREAK
+
+case 164:
+YY_RULE_SETUP
+#line 648 "scan.l"
+format_synerr( _( "bad character: %s" ), yytext );
+ YY_BREAK
+case 165:
+YY_RULE_SETUP
+#line 650 "scan.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+#line 2736 "scan.c"
+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(PERCENT_BRACE_ACTION):
+case YY_STATE_EOF(OPTION):
+case YY_STATE_EOF(LINEDIR):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ 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;
+ 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 single character, 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 = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+ 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_current_buffer->yy_n_chars = 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 =
+ (int) (yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yy_flex_realloc( (void *) b->yy_ch_buf,
+ b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ 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 );
+
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ if ( yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ 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_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+ 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;
+
+ yy_current_state = yy_start;
+ yy_current_state += YY_AT_BOL();
+
+ 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 >= 769 )
+ 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 >= 769 )
+ 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 == 768);
+
+ return yy_is_jam ? 0 : yy_current_state;
+ }
+
+
+#ifndef YY_NO_UNPUT
+#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 += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ yy_current_buffer->yy_n_chars =
+ 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" );
+ }
+
+ *--yy_cp = (char) c;
+
+
+ yytext_ptr = yy_bp;
+ yy_hold_char = *yy_cp;
+ yy_c_buf_p = yy_cp;
+ }
+#endif /* ifndef YY_NO_UNPUT */
+
+
+#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 */
+ int offset = yy_c_buf_p - yytext_ptr;
+ ++yy_c_buf_p;
+
+ switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart( yyin );
+
+ /* fall through */
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap() )
+ return EOF;
+
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p = yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ 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;
+
+ yy_current_buffer->yy_at_bol = (c == '\n');
+
+ 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()" );
+
+ b->yy_is_our_buffer = 1;
+
+ 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 )
+ return;
+
+ if ( b == yy_current_buffer )
+ yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yy_flex_free( (void *) b->yy_ch_buf );
+
+ yy_flex_free( (void *) b );
+ }
+
+
+#ifndef YY_ALWAYS_INTERACTIVE
+#ifndef YY_NEVER_INTERACTIVE
+extern int isatty YY_PROTO(( int ));
+#endif
+#endif
+
+#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
+
+
+ {
+ yy_flush_buffer( b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+#if YY_ALWAYS_INTERACTIVE
+ b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+ b->yy_is_interactive = 0;
+#else
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+ {
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* 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[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == yy_current_buffer )
+ yy_load_buffer_state();
+ }
+
+
+#ifndef YY_NO_SCAN_BUFFER
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+ {
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer( b );
+
+ return b;
+ }
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str )
+#else
+YY_BUFFER_STATE yy_scan_string( yy_str )
+yyconst char *yy_str;
+#endif
+ {
+ int len;
+ for ( len = 0; yy_str[len]; ++len )
+ ;
+
+ return yy_scan_bytes( yy_str, len );
+ }
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+ {
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = len + 2;
+ buf = (char *) yy_flex_alloc( n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < len; ++i )
+ buf[i] = bytes[i];
+
+ buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+ }
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+#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 )
+ {
+ yy_size_t 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);
+ }
+#endif
+
+
+#ifndef YY_NO_POP_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]);
+ }
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state()
+ {
+ return yy_start_stack[yy_start_stack_ptr - 1];
+ }
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+ {
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+ }
+
+
+
+/* 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_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, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+ {
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+ }
+#endif
+
+#ifdef YY_NEED_STRLEN
+#ifdef YY_USE_PROTOS
+static int yy_flex_strlen( yyconst char *s )
+#else
+static int yy_flex_strlen( s )
+yyconst char *s;
+#endif
+ {
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+ }
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+ {
+ return (void *) malloc( size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+ {
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) 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 );
+ }
+
+#if YY_MAIN
+int main()
+ {
+ yylex();
+ return 0;
+ }
+#endif
+#line 650 "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 && strcmp( file, "-" ) )
+ {
+ infilename = copy_string( file );
+ yyin = fopen( infilename, "r" );
+
+ if ( yyin == NULL )
+ lerrsf( _( "can't open %s" ), file );
+ }
+
+ else
+ {
+ yyin = stdin;
+ infilename = copy_string( "<stdin>" );
+ }
+
+ linenum = 1;
+ }
+
+
+/* Wrapper routines for accessing the scanner's malloc routines. */
+
+void *flex_alloc( size )
+size_t size;
+ {
+ return (void *) malloc( size );
+ }
+
+void *flex_realloc( ptr, size )
+void *ptr;
+size_t size;
+ {
+ return (void *) realloc( ptr, size );
+ }
+
+void flex_free( ptr )
+void *ptr;
+ {
+ if ( ptr )
+ free( ptr );
+ }
diff --git a/usr.bin/lex/lex.1 b/usr.bin/lex/lex.1
new file mode 100644
index 0000000..16025e9
--- /dev/null
+++ b/usr.bin/lex/lex.1
@@ -0,0 +1,4062 @@
+.TH FLEX 1 "April 1995" "Version 2.5"
+.SH NAME
+flex \- fast lexical analyzer generator
+.SH SYNOPSIS
+.B flex
+.B [\-bcdfhilnpstvwBFILTV78+? \-C[aefFmr] \-ooutput \-Pprefix \-Sskeleton]
+.B [\-\-help \-\-version]
+.I [filename ...]
+.SH OVERVIEW
+This manual describes
+.I flex,
+a tool for generating programs that perform pattern-matching on text. The
+manual includes both tutorial and reference sections:
+.nf
+
+ Description
+ a brief overview of the tool
+
+ Some Simple Examples
+
+ Format Of The Input File
+
+ Patterns
+ the extended regular expressions used by flex
+
+ How The Input Is Matched
+ the rules for determining what has been matched
+
+ Actions
+ how to specify what to do when a pattern is matched
+
+ The Generated Scanner
+ details regarding the scanner that flex produces;
+ how to control the input source
+
+ Start Conditions
+ introducing context into your scanners, and
+ managing "mini-scanners"
+
+ Multiple Input Buffers
+ how to manipulate multiple input sources; how to
+ scan from strings instead of files
+
+ End-of-file Rules
+ special rules for matching the end of the input
+
+ Miscellaneous Macros
+ a summary of macros available to the actions
+
+ Values Available To The User
+ a summary of values available to the actions
+
+ Interfacing With Yacc
+ connecting flex scanners together with yacc parsers
+
+ Options
+ flex command-line options, and the "%option"
+ directive
+
+ Performance Considerations
+ how to make your scanner go as fast as possible
+
+ Generating C++ Scanners
+ the (experimental) facility for generating C++
+ scanner classes
+
+ Incompatibilities With Lex And POSIX
+ how flex differs from AT&T lex and the POSIX lex
+ standard
+
+ Diagnostics
+ those error messages produced by flex (or scanners
+ it generates) whose meanings might not be apparent
+
+ Files
+ files used by flex
+
+ Deficiencies / Bugs
+ known problems with flex
+
+ See Also
+ other documentation, related tools
+
+ Author
+ includes contact information
+
+.fi
+.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.
+.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 (byte) 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 '*')
+ \\0 a NUL character (ASCII code 0)
+ \\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
+ text matched by s is included when determining
+ whether this rule is the "longest match",
+ but is then returned to the input before
+ the action is executed. So the action only
+ sees the text matched by r. This type
+ of pattern is called trailing context".
+ (There are some combinations of r/s that flex
+ cannot match correctly; see notes in the
+ Deficiencies / Bugs section below regarding
+ "dangerous trailing context".)
+ ^r an r, but only at the beginning of a line (i.e.,
+ which just starting to scan, or right after a
+ newline has been scanned).
+ r$ an r, but only at the end of a line (i.e., just
+ before a newline). Equivalent to "r/\\n".
+
+ Note that flex's notion of "newline" is exactly
+ whatever the C compiler used to compile flex
+ interprets '\\n' as; in particular, on some DOS
+ systems you must either filter out \\r's in the
+ input yourself, or explicitly use r/\\r\\n for "r$".
+
+
+ <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
+In addition to characters and ranges of characters, character classes
+can also contain character class
+.I expressions.
+These are expressions enclosed inside
+.B [:
+and
+.B :]
+delimiters (which themselves must appear between the '[' and ']' of the
+character class; other elements may occur inside the character class, too).
+The valid expressions are:
+.nf
+
+ [:alnum:] [:alpha:] [:blank:]
+ [:cntrl:] [:digit:] [:graph:]
+ [:lower:] [:print:] [:punct:]
+ [:space:] [:upper:] [:xdigit:]
+
+.fi
+These expressions all designate a set of characters equivalent to
+the corresponding standard C
+.B isXXX
+function. For example,
+.B [:alnum:]
+designates those characters for which
+.B isalnum()
+returns true - i.e., any alphabetic or numeric.
+Some systems don't provide
+.B isblank(),
+so flex defines
+.B [:blank:]
+as a blank or a tab.
+.PP
+For example, the following character classes are all equivalent:
+.nf
+
+ [[:alnum:]]
+ [[:alpha:][:digit:]
+ [[:alpha:]0-9]
+ [a-zA-Z0-9]
+
+.fi
+If your scanner is case-insensitive (the
+.B \-i
+flag), then
+.B [:upper:]
+and
+.B [:lower:]
+are equivalent to
+.B [:alpha:].
+.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 unput()
+function destroys 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 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 accommodate large tokens. While this means your
+.B %pointer
+scanner can accommodate 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 c++
+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). This however does not apply when using
+.B %array
+(see above); in that case,
+.B yytext
+may be freely modified in any way.
+.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 of 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".
+.PP
+Two notes regarding use of
+.B yymore().
+First,
+.B yymore()
+depends on the value of
+.I yyleng
+correctly reflecting the size of the current token, so you must not
+modify
+.I yyleng
+if you are using
+.B yymore().
+Second, 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;
+ /* Copy yytext because unput() trashes yytext */
+ char *yycopy = strdup( yytext );
+ unput( ')' );
+ for ( i = yyleng - 1; i >= 0; --i )
+ unput( yycopy[i] );
+ unput( '(' );
+ free( yycopy );
+ }
+
+.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.
+.PP
+An important potential problem when using
+.B unput()
+is that if you are using
+.B %pointer
+(the default), a call to
+.B unput()
+.I destroys
+the contents of
+.I yytext,
+starting with its rightmost character and devouring one character to
+the left with each call. If you need the value of yytext preserved
+after a call to
+.B unput()
+(as in the above example),
+you must either first copy it elsewhere, or build your scanner using
+.B %array
+instead (see How The Input Is Matched).
+.PP
+Finally, 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 YY_FLUSH_BUFFER
+flushes the scanner's internal buffer
+so that the next time the scanner attempts to match a token, it will
+first refill the buffer using
+.B YY_INPUT
+(see The Generated Scanner, below). This action is a special case
+of the more general
+.B yy_flush_buffer()
+function, described below in the section Multiple Input Buffers.
+.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 (which can be nil, if you've set up
+.B YY_INPUT
+to scan from a source other than
+.I yyin),
+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;
+but better is to use
+.B YY_FLUSH_BUFFER
+(see above).
+Note that
+.B yyrestart()
+does
+.I not
+reset the start condition to
+.B INITIAL
+(see Start Conditions, below).
+.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
+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. Note that in either case, the start condition remains unchanged;
+it does
+.I not
+revert to
+.B INITIAL.
+.PP
+If you do not supply your own version of
+.B yywrap(),
+then you must either use
+.B %option noyywrap
+(in which case the scanner behaves as though
+.B yywrap()
+returned 1), or you must link with
+.B \-ll
+to obtain the default version of the routine, which always returns 1.
+.PP
+Three routines are available for scanning from in-memory buffers rather
+than files:
+.B yy_scan_string(), yy_scan_bytes(),
+and
+.B yy_scan_buffer().
+See the discussion of them below in the section Multiple Input Buffers.
+.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();
+
+ bar something_else();
+
+.fi
+is equivalent to
+.nf
+
+ %x example
+ %%
+
+ <example>foo do_something();
+
+ <INITIAL,example>bar something_else();
+
+.fi
+Without the
+.B <INITIAL,example>
+qualifier, the
+.I bar
+pattern in the second example wouldn't be active (i.e., couldn't match)
+when in start condition
+.B example.
+If we just used
+.B <example>
+to qualify
+.I bar,
+though, then it would only be active in
+.B example
+and not in
+.B INITIAL,
+while in the first example it's active in both, because in the first
+example the
+.B example
+startion condition is an
+.I inclusive
+.B (%s)
+start condition.
+.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
+ %%
+
+ <example>foo do_something();
+
+ <*>bar something_else();
+
+.fi
+.PP
+The default rule (to
+.B ECHO
+any unmatched character) remains active in start conditions. It
+is equivalent to:
+.nf
+
+ <*>.|\\n ECHO;
+
+.fi
+.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
+.B 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
+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
+Flex provides
+.B YYSTATE
+as an alias for
+.B YY_START
+(since that is what's used by AT&T
+.I lex).
+.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 *yptr = yytext;
+
+ while ( *yptr )
+ *string_buf_ptr++ = *yptr++;
+ }
+
+.fi
+.PP
+Often, such as in some of the examples above, you wind up writing a
+whole bunch of rules all preceded by the same start condition(s). Flex
+makes this a little easier and cleaner by introducing a notion of
+start condition
+.I scope.
+A start condition scope is begun with:
+.nf
+
+ <SCs>{
+
+.fi
+where
+.I SCs
+is a list of one or more start conditions. Inside the start condition
+scope, every rule automatically has the prefix
+.I <SCs>
+applied to it, until a
+.I '}'
+which matches the initial
+.I '{'.
+So, for example,
+.nf
+
+ <ESC>{
+ "\\\\n" return '\\n';
+ "\\\\r" return '\\r';
+ "\\\\f" return '\\f';
+ "\\\\0" return '\\0';
+ }
+
+.fi
+is equivalent to:
+.nf
+
+ <ESC>"\\\\n" return '\\n';
+ <ESC>"\\\\r" return '\\r';
+ <ESC>"\\\\f" return '\\f';
+ <ESC>"\\\\0" return '\\0';
+
+.fi
+Start condition scopes may be nested.
+.PP
+Three routines are available for manipulating stacks of start conditions:
+.TP
+.B void yy_push_state(int new_state)
+pushes the current start condition onto the top of the start condition
+stack and switches to
+.I new_state
+as though you had used
+.B BEGIN new_state
+(recall that start condition names are also integers).
+.TP
+.B void yy_pop_state()
+pops the top of the stack and switches to it via
+.B BEGIN.
+.TP
+.B int yy_top_state()
+returns the top of the stack without altering the stack's contents.
+.PP
+The start condition stack grows dynamically and so has no built-in
+size limitation. If memory is exhausted, program execution aborts.
+.PP
+To use start condition stacks, your scanner must include a
+.B %option stack
+directive (see Options below).
+.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 (see below). The
+.B YY_BUFFER_STATE
+type is a pointer to an opaque
+.B struct yy_buffer_state
+structure, so you may safely initialize YY_BUFFER_STATE variables to
+.B ((YY_BUFFER_STATE) 0)
+if you wish, and also refer to the opaque structure in order to
+correctly declare input buffers in source files other than that
+of your scanner. Note that the
+.I FILE
+pointer in the call to
+.B yy_create_buffer
+is only used as the value of
+.I yyin
+seen by
+.B YY_INPUT;
+if you redefine
+.B YY_INPUT
+so it no longer uses
+.I yyin,
+then you can safely pass a nil
+.I FILE
+pointer to
+.B yy_create_buffer.
+You select a particular buffer to scan from using:
+.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. Note also that switching input sources via either
+.B yy_switch_to_buffer()
+or
+.B yywrap()
+does
+.I not
+change the start condition.
+.nf
+
+ void yy_delete_buffer( YY_BUFFER_STATE buffer )
+
+.fi
+is used to reclaim the storage associated with a buffer. (
+.B buffer
+can be nil, in which case the routine does nothing.)
+You can also clear the current contents of a buffer using:
+.nf
+
+ void yy_flush_buffer( YY_BUFFER_STATE buffer )
+
+.fi
+This function discards the buffer's contents,
+so the next time the scanner attempts to match a token from the
+buffer, it will first fill the buffer anew using
+.B YY_INPUT.
+.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
+Three routines are available for setting up input buffers for
+scanning in-memory strings instead of files. All of them create
+a new input buffer for scanning the string, and return a corresponding
+.B YY_BUFFER_STATE
+handle (which you should delete with
+.B yy_delete_buffer()
+when done with it). They also switch to the new buffer using
+.B yy_switch_to_buffer(),
+so the next call to
+.B yylex()
+will start scanning the string.
+.TP
+.B yy_scan_string(const char *str)
+scans a NUL-terminated string.
+.TP
+.B yy_scan_bytes(const char *bytes, int len)
+scans
+.I len
+bytes (including possibly NUL's)
+starting at location
+.I bytes.
+.PP
+Note that both of these functions create and scan a
+.I copy
+of the string or bytes. (This may be desirable, since
+.B yylex()
+modifies the contents of the buffer it is scanning.) You can avoid the
+copy by using:
+.TP
+.B yy_scan_buffer(char *base, yy_size_t size)
+which scans in place the buffer starting at
+.I base,
+consisting of
+.I size
+bytes, the last two bytes of which
+.I must
+be
+.B YY_END_OF_BUFFER_CHAR
+(ASCII NUL).
+These last two bytes are not scanned; thus, scanning
+consists of
+.B base[0]
+through
+.B base[size-2],
+inclusive.
+.IP
+If you fail to set up
+.I base
+in this manner (i.e., forget the final two
+.B YY_END_OF_BUFFER_CHAR
+bytes), then
+.B yy_scan_buffer()
+returns a nil pointer instead of creating a new input buffer.
+.IP
+The type
+.B yy_size_t
+is an integral type to which you can cast an integer expression
+reflecting the size of the buffer.
+.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
+.B 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.
+When
+.B YY_USER_ACTION
+is invoked, the variable
+.I yy_act
+gives the number of the matched rule (rules are numbered starting with 1).
+Suppose you want to profile how often each of your rules is matched. The
+following would do the trick:
+.nf
+
+ #define YY_USER_ACTION ++ctr[yy_act]
+
+.fi
+where
+.I ctr
+is an array to hold the counts for the different rules. Note that
+the macro
+.B YY_NUM_RULES
+gives the total number of rules (including the default rule, even if
+you use
+.B \-s),
+so a correct declaration for
+.I ctr
+is:
+.nf
+
+ int ctr[YY_NUM_RULES];
+
+.fi
+.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
+The macro
+.B yy_set_interactive(is_interactive)
+can be used to control whether the current buffer is considered
+.I interactive.
+An interactive buffer is processed more slowly,
+but must be used when the scanner's input source is indeed
+interactive to avoid problems due to waiting to fill buffers
+(see the discussion of the
+.B \-I
+flag below). A non-zero value
+in the macro invocation marks the buffer as interactive, a zero
+value as non-interactive. Note that use of this macro overrides
+.B %option always-interactive
+or
+.B %option never-interactive
+(see Options below).
+.B yy_set_interactive()
+must be invoked prior to beginning to scan the buffer that is
+(or is not) to be considered interactive.
+.PP
+The macro
+.B yy_set_bol(at_bol)
+can be used to control whether the current buffer's scanning
+context for the next token match is done as though at the
+beginning of a line. A non-zero macro argument makes rules anchored with
+'^' active, while a zero argument makes '^' rules inactive.
+.PP
+The macro
+.B YY_AT_BOL()
+returns true if the next token scanned from the current buffer
+will have '^' rules active, false otherwise.
+.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 VALUES AVAILABLE TO THE USER
+This section summarizes the various values available to the user
+in the rule actions.
+.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).
+.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, 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 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
+.I 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.
+.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 stdout
+and then exits.
+.B \-?
+and
+.B \-\-help
+are synonyms for
+.B \-h.
+.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. This option also results
+in the name
+.B YY_FLEX_LEX_COMPAT
+being #define'd in the generated scanner.
+.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,
+.B %option yylineno,
+and variable trailing context (see the Deficiencies / Bugs section below)
+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 either the original
+.I flex
+input file (if the errors are due to code in the input file), or
+.B lex.yy.c
+(if the errors are
+.I flex's
+fault -- you should report these sorts of errors to the email address
+given below).
+.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 stdout
+and exits.
+.B \-\-version
+is a synonym for
+.B \-V.
+.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 units 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 \-ooutput
+directs flex to write the scanner to the file
+.B output
+instead of
+.B lex.yy.c.
+If you combine
+.B \-o
+with the
+.B \-t
+option, then the scanner is written to
+.I stdout
+but its
+.B #line
+directives (see the
+.B \\-L
+option above) refer to the file
+.B output.
+.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
+
+ yy_create_buffer
+ yy_delete_buffer
+ yy_flex_debug
+ yy_init_buffer
+ yy_flush_buffer
+ yy_load_buffer_state
+ yy_switch_to_buffer
+ yyin
+ yyleng
+ yylex
+ yylineno
+ yyout
+ yyrestart
+ yytext
+ yywrap
+
+.fi
+(If you are using a C++ scanner, then only
+.B yywrap
+and
+.B yyFlexLexer
+are affected.)
+Within your scanner itself, you can still refer to the global variables
+and functions using either version of their name; but externally, 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
+either
+provide your own (appropriately-named) version of the routine for your
+scanner, or use
+.B %option noyywrap,
+as linking with
+.B \-ll
+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.
+.PP
+.I flex
+also provides a mechanism for controlling options within the
+scanner specification itself, rather than from the flex command-line.
+This is done by including
+.B %option
+directives in the first section of the scanner specification.
+You can specify multiple options with a single
+.B %option
+directive, and multiple directives in the first section of your flex input
+file.
+.PP
+Most options are given simply as names, optionally preceded by the
+word "no" (with no intervening whitespace) to negate their meaning.
+A number are equivalent to flex flags or their negation:
+.nf
+
+ 7bit -7 option
+ 8bit -8 option
+ align -Ca option
+ backup -b option
+ batch -B option
+ c++ -+ option
+
+ caseful or
+ case-sensitive opposite of -i (default)
+
+ case-insensitive or
+ caseless -i option
+
+ debug -d option
+ default opposite of -s option
+ ecs -Ce option
+ fast -F option
+ full -f option
+ interactive -I option
+ lex-compat -l option
+ meta-ecs -Cm option
+ perf-report -p option
+ read -Cr option
+ stdout -t option
+ verbose -v option
+ warn opposite of -w option
+ (use "%option nowarn" for -w)
+
+ array equivalent to "%array"
+ pointer equivalent to "%pointer" (default)
+
+.fi
+Some
+.B %option's
+provide features otherwise not available:
+.TP
+.B always-interactive
+instructs flex to generate a scanner which always considers its input
+"interactive". Normally, on each new input file the scanner calls
+.B isatty()
+in an attempt to determine whether
+the scanner's input source is interactive and thus should be read a
+character at a time. When this option is used, however, then no
+such call is made.
+.TP
+.B main
+directs flex to provide a default
+.B main()
+program for the scanner, which simply calls
+.B yylex().
+This option implies
+.B noyywrap
+(see below).
+.TP
+.B never-interactive
+instructs flex to generate a scanner which never considers its input
+"interactive" (again, no call made to
+.B isatty()).
+This is the opposite of
+.B always-interactive.
+.TP
+.B stack
+enables the use of start condition stacks (see Start Conditions above).
+.TP
+.B stdinit
+if set (i.e.,
+.B %option stdinit)
+initializes
+.I yyin
+and
+.I yyout
+to
+.I stdin
+and
+.I stdout,
+instead of the default of
+.I nil.
+Some existing
+.I lex
+programs depend on this behavior, even though it is not compliant with
+ANSI C, which does not require
+.I stdin
+and
+.I stdout
+to be compile-time constant.
+.TP
+.B yylineno
+directs
+.I flex
+to generate a scanner that maintains the number of the current line
+read from its input in the global variable
+.B yylineno.
+This option is implied by
+.B %option lex-compat.
+.TP
+.B yywrap
+if unset (i.e.,
+.B %option noyywrap),
+makes the scanner not call
+.B yywrap()
+upon an end-of-file, but simply assume that there are no more
+files to scan (until the user points
+.I yyin
+at a new file and calls
+.B yylex()
+again).
+.PP
+.I flex
+scans your rule actions to determine whether you use the
+.B REJECT
+or
+.B yymore()
+features. The
+.B reject
+and
+.B yymore
+options are available to override its decision as to whether you use the
+options, either by setting them (e.g.,
+.B %option reject)
+to indicate the feature is indeed used, or
+unsetting them to indicate it actually is not used
+(e.g.,
+.B %option noyymore).
+.PP
+Three options take string-delimited values, offset with '=':
+.nf
+
+ %option outfile="ABC"
+
+.fi
+is equivalent to
+.B -oABC,
+and
+.nf
+
+ %option prefix="XYZ"
+
+.fi
+is equivalent to
+.B -PXYZ.
+Finally,
+.nf
+
+ %option yyclass="foo"
+
+.fi
+only applies when generating a C++ scanner (
+.B \-+
+option). It informs
+.I flex
+that you have derived
+.B foo
+as a subclass of
+.B yyFlexLexer,
+so
+.I flex
+will place your actions in the member function
+.B foo::yylex()
+instead of
+.B yyFlexLexer::yylex().
+It also generates a
+.B yyFlexLexer::yylex()
+member function that emits a run-time error (by invoking
+.B yyFlexLexer::LexerError())
+if called.
+See Generating C++ Scanners, below, for additional information.
+.PP
+A number of options are available for lint purists who want to suppress
+the appearance of unneeded routines in the generated scanner. Each of the
+following, if unset
+(e.g.,
+.B %option nounput
+), results in the corresponding routine not appearing in
+the generated scanner:
+.nf
+
+ input, unput
+ yy_push_state, yy_pop_state, yy_top_state
+ yy_scan_buffer, yy_scan_bytes, yy_scan_string
+
+.fi
+(though
+.B yy_push_state()
+and friends won't appear anyway unless you use
+.B %option stack).
+.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
+ %option yylineno
+ arbitrary trailing context
+
+ pattern sets that require backing up
+ %array
+ %option interactive
+ %option always-interactive
+
+ '^' beginning-of-line operator
+ yymore()
+
+.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
+It's important to keep in mind that you gain the benefits of eliminating
+backing up only if you eliminate
+.I every
+instance of backing up. Leaving just one means you gain nothing.
+.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
+Deficiencies / Bugs below).
+.LP
+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.
+.PP
+Another final note regarding performance: as mentioned above in the section
+How the Input is Matched, dynamically resizing
+.B yytext
+to accommodate 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.
+.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 (or, equivalently,
+.B %option c++),
+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.
+.TP
+.B int lineno() const
+returns the current input line number
+(see
+.B %option yylineno),
+or
+.B 1
+if
+.B %option yylineno
+was not used.
+.TP
+.B void set_debug( int flag )
+sets the debugging flag for the scanner, equivalent to assigning to
+.B yy_flex_debug
+(see the Options section above). Note that you must build the scanner
+using
+.B %option debug
+to include debugging information in it.
+.TP
+.B int debug() const
+returns the current setting of the debugging flag.
+.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_flush_buffer(),
+.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. If you derive a subclass
+.B S
+from
+.B yyFlexLexer
+and want to access the member functions and variables of
+.B S
+inside
+.B yylex(),
+then you need to use
+.B %option yyclass="S"
+to inform
+.I flex
+that you will be using that subclass instead of
+.B yyFlexLexer.
+In this case, rather than generating
+.B yyFlexLexer::yylex(),
+.I flex
+generates
+.B S::yylex()
+(and also generates a dummy
+.B yyFlexLexer::yylex()
+that calls
+.B yyFlexLexer::LexerError()
+if called).
+.TP
+.B
+virtual void switch_streams(istream* new_in = 0,
+.B
+ostream* new_out = 0)
+reassigns
+.B yyin
+to
+.B new_in
+(if non-nil)
+and
+.B yyout
+to
+.B new_out
+(ditto), deleting the previous input buffer if
+.B yyin
+is reassigned.
+.TP
+.B
+int yylex( istream* new_in, ostream* new_out = 0 )
+first switches the input streams via
+.B switch_streams( new_in, new_out )
+and then returns the value of
+.B yylex().
+.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
+If you want to create multiple (different) lexer classes, you use the
+.B \-P
+flag (or the
+.B prefix=
+option) to rename each
+.B yyFlexLexer
+to some other
+.B xxFlexLexer.
+You then can include
+.B <FlexLexer.h>
+in your other sources once per lexer class, first renaming
+.B yyFlexLexer
+as follows:
+.nf
+
+ #undef yyFlexLexer
+ #define yyFlexLexer xxFlexLexer
+ #include <FlexLexer.h>
+
+ #undef yyFlexLexer
+ #define yyFlexLexer zzFlexLexer
+ #include <FlexLexer.h>
+
+.fi
+if, for example, you used
+.B %option prefix="xx"
+for one of your scanners and
+.B %option prefix="zz"
+for the other.
+.PP
+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. Flex is fully compliant with the POSIX
+.I lex
+specification, except that when using
+.B %pointer
+(the default), a call to
+.B unput()
+destroys the contents of
+.B yytext,
+which is counter to the POSIX specification.
+.PP
+In this section we discuss all of the known areas of incompatibility
+between flex, AT&T lex, and the POSIX specification.
+.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
+or
+.B %option yylineno
+is used.
+.IP
+.B yylineno
+should be maintained on a per-buffer basis, rather than a per-scanner
+(single global variable) basis.
+.IP
+.B 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 -
+The
+.B unput()
+routine is not redefinable. This restriction is in accordance with POSIX.
+.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 -
+Some implementations of
+.I lex
+allow a rule's action to begin on a separate line, if the rule's pattern
+has trailing whitespace:
+.nf
+
+ %%
+ foo|bar<space here>
+ { foobar_action(); }
+
+.fi
+.I flex
+does not support this feature.
+.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
+is 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 -
+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.
+Scanners also include
+.B YY_FLEX_MAJOR_VERSION
+and
+.B YY_FLEX_MINOR_VERSION
+indicating which version of
+.I flex
+generated the scanner
+(for example, for the 2.5 release, these defines would be 2 and 5
+respectively).
+.PP
+The following
+.I flex
+features are not included in
+.I lex
+or the POSIX specification:
+.nf
+
+ C++ scanners
+ %option
+ start condition scopes
+ start condition stacks
+ interactive/non-interactive scanners
+ yy_scan_string() and friends
+ yyterminate()
+ yy_set_interactive()
+ yy_set_bol()
+ YY_AT_BOL()
+ <<EOF>>
+ <*>
+ YY_DECL
+ YY_START
+ YY_USER_ACTION
+ YY_USER_INIT
+ #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). Use
+.B %option reject
+or
+.B %option yymore
+to indicate to flex that you really do use these features.
+.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
+.TP
+.B \-ll
+library with which scanners must be linked.
+.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 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()
+invalidates yytext and yyleng, unless the
+.B %array
+directive
+or the
+.B \-l
+option has been used.
+.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
+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.
+.SH SEE ALSO
+.PP
+lex(1), yacc(1), sed(1), awk(1).
+.PP
+John Levine, Tony Mason, and Doug Brown,
+.I Lex & Yacc,
+O'Reilly and Associates. Be sure to get the 2nd edition.
+.PP
+M. E. Lesk and E. Schmidt,
+.I LEX \- Lexical Analyzer Generator
+.PP
+Alfred Aho, Ravi Sethi and Jeffrey Ullman,
+.I Compilers: Principles, Techniques and Tools,
+Addison-Wesley (1986). Describes the pattern-matching techniques used by
+.I flex
+(deterministic finite automata).
+.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,
+Robert Abramovitz,
+Stan Adermann, Terry Allen, David Barker-Plummer, John Basrai,
+Neal Becker, Nelson H.F. Beebe, benson@odi.com,
+Karl Berry, Peter A. Bigot, Simon Blanchard,
+Keith Bostic, Frederic Brehm, Ian Brockbank, Kin Cho, Nick Christopher,
+Brian Clapper, J.T. Conklin,
+Jason Coughlin, Bill Cox, Nick Cropper, Dave Curtis, Scott David
+Daniels, Chris G. Demetriou, Theo Deraadt,
+Mike Donahue, Chuck Doucette, Tom Epperly, Leo Eskin,
+Chris Faylor, Chris Flatters, Jon Forrest, Jeffrey Friedl,
+Joe Gayda, Kaveh R. Ghazi, Wolfgang Glunz,
+Eric Goldman, Christopher M. Gould, Ulrich Grepel, Peer Griebel,
+Jan Hajic, Charles Hemphill, NORO Hideo,
+Jarkko Hietaniemi, Scott Hofmann,
+Jeff Honig, Dana Hudes, Eric Hughes, John Interrante,
+Ceriel Jacobs, Michal Jaegermann, Sakari Jalovaara, Jeffrey R. Jones,
+Henry Juengst, Klaus Kaempf, Jonathan I. Kamens, Terrence O Kane,
+Amir Katz, ken@ken.hilco.com, Kevin B. Kenny,
+Steve Kirsch, Winfried Koenig, Marq Kole, Ronald Lamprecht,
+Greg Lee, Rohan Lenard, Craig Leres, John Levine, Steve Liddle,
+David Loffredo, Mike Long,
+Mohamed el Lozy, Brian Madsen, Malte, Joe Marshall,
+Bengt Martensson, Chris Metcalf,
+Luke Mewburn, Jim Meyering, R. Alexander Milowski, Erik Naggum,
+G.T. Nicol, Landon Noll, James Nordby, Marc Nozell,
+Richard Ohnemus, Karsten Pahnke,
+Sven Panne, Roland Pesch, Walter Pelissero, Gaumond
+Pierre, Esmond Pitt, Jef Poskanzer, Joe Rahmeh, Jarmo Raiha,
+Frederic Raimbault, Pat Rankin, Rick Richardson,
+Kevin Rodgers, Kai Uwe Rommel, Jim Roskind, Alberto Santini,
+Andreas Scherer, Darrell Schiebel, Raf Schietekat,
+Doug Schmidt, Philippe Schnoebelen, Andreas Schwab,
+Larry Schwimmer, Alex Siegel, Eckehard Stolz, Jan-Erik Strvmquist,
+Mike Stump, Paul Stuart, Dave Tallman, Ian Lance Taylor,
+Chris Thewalt, Richard M. Timoney, Jodi Tsai,
+Paul Tuinenga, Gary Weik, Frank Whaley, Gerhard Wilhelms, Kent Williams, Ken
+Yap, Ron Zellar, 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 vern@ee.lbl.gov.
diff --git a/usr.bin/lex/lib/Makefile b/usr.bin/lex/lib/Makefile
new file mode 100644
index 0000000..8fd744f
--- /dev/null
+++ b/usr.bin/lex/lib/Makefile
@@ -0,0 +1,16 @@
+# $Id: Makefile,v 1.5 1997/02/22 19:55:36 peter Exp $
+
+LIB= ln
+SRCS= libmain.c libyywrap.c
+NOPIC= yes
+
+LINKS= ${LIBDIR}/libln.a ${LIBDIR}/libl.a
+LINKS+= ${LIBDIR}/libln.a ${LIBDIR}/libfl.a
+
+.if !defined(NOPROFILE)
+LINKS+= ${LIBDIR}/libln_p.a ${LIBDIR}/libl_p.a
+LINKS+= ${LIBDIR}/libln_p.a ${LIBDIR}/libfl_p.a
+.endif
+
+.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..6c43b08
--- /dev/null
+++ b/usr.bin/lex/lib/libmain.c
@@ -0,0 +1,15 @@
+/* libmain - flex run-time support library "main" function */
+
+/* $Header: /home/daffy/u0/vern/flex/RCS/libmain.c,v 1.4 95/09/27 12:47:55 vern Exp $ */
+
+extern int yylex();
+
+int main( argc, argv )
+int argc;
+char *argv[];
+ {
+ while ( yylex() != 0 )
+ ;
+
+ return 0;
+ }
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..bc1e8c9
--- /dev/null
+++ b/usr.bin/lex/main.c
@@ -0,0 +1,1177 @@
+/* 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.2 1996/06/19 20:26:16 nate 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));
+
+#ifdef NEED_ARGV_FIXUP
+extern void argv_fixup PROTO((int *, char ***));
+#endif
+
+
+/* these globals are all defined and commented in flexdef.h */
+int printstats, syntaxerror, eofseen, ddebug, trace, nowarn, spprdflt;
+int interactive, caseins, lex_compat, do_yylineno, useecs, fulltbl, usemecs;
+int fullspd, gen_line_dirs, performance_report, backing_up_report;
+int C_plus_plus, long_align, use_read, yytext_is_array, do_yywrap, csize;
+int yymore_used, reject, real_reject, continued_action, in_rule;
+int yymore_really_used, reject_really_used;
+int datapos, dataline, linenum, out_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, *outfilename = NULL;
+int did_outfilename;
+char *prefix, *yyclass;
+int do_stdinit, use_stdout;
+int onestate[ONE_STACK_SIZE], onesym[ONE_STACK_SIZE];
+int onenext[ONE_STACK_SIZE], onedef[ONE_STACK_SIZE], onesp;
+int current_mns, current_max_rules;
+int num_rules, num_eof_rules, default_rule, 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, *scset, *scbol, *scxclu, *sceof;
+int current_max_scs;
+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, *cclmap, *ccllen, *cclng, cclreuse;
+int current_maxccls, 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;
+
+/* Make sure program_name is initialized so we don't crash if writing
+ * out an error message before getting the program name from argv[0].
+ */
+char *program_name = "flex";
+
+#ifndef SHORT_FILE_NAMES
+static char *outfile_template = "lex.%s.%s";
+static char *backing_name = "lex.backup";
+#else
+static char *outfile_template = "lex%s.%s";
+static char *backing_name = "lex.bck";
+#endif
+
+#ifdef THINK_C
+#include <console.h>
+#endif
+
+#ifdef MS_DOS
+extern unsigned _stklen = 16384;
+#endif
+
+static char outfile_path[MAXLINE];
+static int outfile_created = 0;
+static char *skelname = NULL;
+
+
+int main( argc, argv )
+int argc;
+char **argv;
+ {
+ int i;
+
+#ifdef THINK_C
+ argc = ccommand( &argv );
+#endif
+#ifdef NEED_ARGV_FIXUP
+ argv_fixup( &argc, &argv );
+#endif
+
+ 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 */
+ }
+
+
+/* check_options - check user-specified options */
+
+void check_options()
+ {
+ int i;
+
+ 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;
+ do_yylineno = true;
+ use_read = false;
+ }
+
+ if ( do_yylineno )
+ /* This should really be "maintain_backup_tables = true" */
+ reject_really_used = true;
+
+ if ( csize == unspecified )
+ {
+ if ( (fulltbl || fullspd) && ! useecs )
+ csize = DEFAULT_CSIZE;
+ else
+ csize = CSIZE;
+ }
+
+ if ( interactive == unspecified )
+ {
+ if ( fulltbl || fullspd )
+ interactive = false;
+ else
+ interactive = true;
+ }
+
+ if ( fulltbl || fullspd )
+ {
+ if ( usemecs )
+ flexerror(
+ _( "-Cf/-CF and -Cm don't make sense together" ) );
+
+ if ( interactive )
+ flexerror( _( "-Cf/-CF and -I are incompatible" ) );
+
+ if ( lex_compat )
+ flexerror(
+ _( "-Cf/-CF are incompatible with lex-compatibility mode" ) );
+
+ if ( do_yylineno )
+ flexerror(
+ _( "-Cf/-CF and %option yylineno 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 ( C_plus_plus && yytext_is_array )
+ {
+ warn( _( "%array incompatible with -+ option" ) );
+ yytext_is_array = false;
+ }
+
+ 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 */
+ }
+ }
+
+ if ( ! use_stdout )
+ {
+ FILE *prev_stdout;
+
+ if ( ! did_outfilename )
+ {
+ char *suffix;
+
+ if ( C_plus_plus )
+ suffix = "cc";
+ else
+ suffix = "c";
+
+ sprintf( outfile_path, outfile_template,
+ prefix, suffix );
+
+ outfilename = outfile_path;
+ }
+
+ prev_stdout = freopen( outfilename, "w", stdout );
+
+ if ( prev_stdout == NULL )
+ lerrsf( _( "could not create %s" ), outfilename );
+
+ outfile_created = 1;
+ }
+
+ if ( skelname && (skelfile = fopen( skelname, "r" )) == NULL )
+ lerrsf( _( "can't open skeleton file %s" ), skelname );
+
+ if ( strcmp( prefix, "yy" ) )
+ {
+#define GEN_PREFIX(name) out_str3( "#define yy%s %s%s\n", name, prefix, name )
+ if ( C_plus_plus )
+ GEN_PREFIX( "FlexLexer" );
+ else
+ {
+ GEN_PREFIX( "_create_buffer" );
+ GEN_PREFIX( "_delete_buffer" );
+ GEN_PREFIX( "_scan_buffer" );
+ GEN_PREFIX( "_scan_string" );
+ GEN_PREFIX( "_scan_bytes" );
+ GEN_PREFIX( "_flex_debug" );
+ GEN_PREFIX( "_init_buffer" );
+ GEN_PREFIX( "_flush_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" );
+
+ if ( do_yylineno )
+ GEN_PREFIX( "lineno" );
+ }
+
+ if ( do_yywrap )
+ GEN_PREFIX( "wrap" );
+
+ outn( "" );
+ }
+
+ if ( did_outfilename )
+ line_directive_out( stdout, 0 );
+
+ skelout();
+ }
+
+
+/* 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 ) )
+ lerrsf( _( "input error reading skeleton file %s" ),
+ skelname );
+
+ else if ( fclose( skelfile ) )
+ lerrsf( _( "error closing skeleton file %s" ),
+ skelname );
+ }
+
+ if ( exit_status != 0 && outfile_created )
+ {
+ if ( ferror( stdout ) )
+ lerrsf( _( "error writing output file %s" ),
+ outfilename );
+
+ else if ( fclose( stdout ) )
+ lerrsf( _( "error closing output file %s" ),
+ outfilename );
+
+ else if ( unlink( outfilename ) )
+ lerrsf( _( "error deleting output file %s" ),
+ outfilename );
+ }
+
+ 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 ) )
+ lerrsf( _( "error writing backup file %s" ),
+ backing_name );
+
+ else if ( fclose( backing_up_file ) )
+ lerrsf( _( "error closing backup file %s" ),
+ backing_name );
+ }
+
+ 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 == false )
+ putc( 'B', stderr );
+ if ( interactive == true )
+ putc( 'I', stderr );
+ if ( ! gen_line_dirs )
+ putc( 'L', stderr );
+ if ( trace )
+ putc( 'T', stderr );
+
+ if ( csize == unspecified )
+ /* We encountered an error fairly early on, so csize
+ * never got specified. Define it now, to prevent
+ * bogus table sizes being written out below.
+ */
+ csize = 256;
+
+ 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 ( did_outfilename )
+ fprintf( stderr, " -o%s", outfilename );
+
+ 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 );
+ }
+
+ exit( exit_status );
+ }
+
+
+/* flexinit - initialize flex */
+
+void flexinit( argc, argv )
+int argc;
+char **argv;
+ {
+ int i, sawcmpflag;
+ char *arg;
+
+ printstats = syntaxerror = trace = spprdflt = caseins = false;
+ lex_compat = C_plus_plus = backing_up_report = ddebug = fulltbl = false;
+ fullspd = long_align = nowarn = yymore_used = continued_action = false;
+ do_yylineno = yytext_is_array = in_rule = reject = do_stdinit = false;
+ yymore_really_used = reject_really_used = unspecified;
+ interactive = csize = unspecified;
+ do_yywrap = gen_line_dirs = usemecs = useecs = true;
+ performance_report = 0;
+ did_outfilename = 0;
+ prefix = "yy";
+ yyclass = 0;
+ use_read = use_stdout = false;
+
+ sawcmpflag = 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 )
+ {
+ arg = argv[0];
+
+ if ( arg[0] != '-' || arg[1] == '\0' )
+ break;
+
+ if ( arg[1] == '-' )
+ { /* --option */
+ if ( ! strcmp( arg, "--help" ) )
+ arg = "-h";
+
+ else if ( ! strcmp( arg, "--version" ) )
+ arg = "-V";
+
+ else if ( ! strcmp( arg, "--" ) )
+ { /* end of options */
+ --argc;
+ ++argv;
+ break;
+ }
+ }
+
+ for ( i = 1; arg[i] != '\0'; ++i )
+ switch ( arg[i] )
+ {
+ case '+':
+ C_plus_plus = true;
+ break;
+
+ case 'B':
+ interactive = false;
+ break;
+
+ case 'b':
+ backing_up_report = true;
+ break;
+
+ case 'c':
+ break;
+
+ 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 '?':
+ case 'h':
+ usage();
+ exit( 0 );
+
+ case 'I':
+ interactive = 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 'o':
+ if ( i != 1 )
+ flexerror(
+ _( "-o flag must be given separately" ) );
+
+ outfilename = arg + i + 1;
+ did_outfilename = 1;
+ goto get_next_arg;
+
+ 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':
+ printf( _( "%s version %s\n" ),
+ program_name, flex_version );
+ exit( 0 );
+
+ case 'w':
+ nowarn = true;
+ break;
+
+ case '7':
+ csize = 128;
+ break;
+
+ case '8':
+ csize = CSIZE;
+ break;
+
+ default:
+ fprintf( stderr,
+ _( "%s: unknown flag '%c'. For usage, try\n\t%s --help\n" ),
+ program_name, (int) arg[i],
+ program_name );
+ exit( 1 );
+ }
+
+ /* Used by -C, -S, -o, and -P flags in lieu of a "continue 2"
+ * control.
+ */
+ get_next_arg: ;
+ }
+
+ num_input_files = argc;
+ input_files = argv;
+ set_input_file( num_input_files > 0 ? input_files[0] : NULL );
+
+ lastccl = lastsc = 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;
+
+ out_linenum = linenum = sectnum = 1;
+ firstprot = NIL;
+
+ /* Used in mkprot() so that the first proto goes in slot 1
+ * of the proto queue.
+ */
+ lastprot = 1;
+
+ set_up_initial_allocations();
+ }
+
+
+/* readin - read in the rules section of the input file(s) */
+
+void readin()
+ {
+ static char yy_stdinit[] = "FILE *yyin = stdin, *yyout = stdout;";
+ static char yy_nostdinit[] =
+ "FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;";
+
+ line_directive_out( (FILE *) 0, 1 );
+
+ if ( yyparse() )
+ {
+ pinpoint_message( _( "fatal parse error" ) );
+ flexend( 1 );
+ }
+
+ if ( syntaxerror )
+ flexend( 1 );
+
+ if ( backing_up_report )
+ {
+ backing_up_file = fopen( backing_name, "w" );
+ if ( backing_up_file == NULL )
+ lerrsf(
+ _( "could not create backing-up info file %s" ),
+ backing_name );
+ }
+
+ else
+ backing_up_file = NULL;
+
+ if ( yymore_really_used == true )
+ yymore_used = true;
+ else if ( yymore_really_used == false )
+ yymore_used = false;
+
+ if ( reject_really_used == true )
+ reject = true;
+ else if ( reject_really_used == false )
+ 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" ) );
+ }
+
+ else if ( do_yylineno )
+ {
+ fprintf( stderr,
+ _( "%%option yylineno entails a large performance penalty\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 if ( do_yylineno )
+ flexerror(
+ _( "%option yylineno cannot be used with -f or -F" ) );
+ else
+ flexerror(
+ _( "variable trailing context rules cannot be used with -f or -F" ) );
+ }
+
+ if ( reject )
+ outn( "\n#define YY_USES_REJECT" );
+
+ if ( ! do_yywrap )
+ {
+ outn( "\n#define yywrap() 1" );
+ outn( "#define YY_SKIP_YYWRAP" );
+ }
+
+ if ( ddebug )
+ outn( "\n#define FLEX_DEBUG" );
+
+ if ( csize == 256 )
+ outn( "typedef unsigned char YY_CHAR;" );
+ else
+ outn( "typedef char YY_CHAR;" );
+
+ if ( C_plus_plus )
+ {
+ outn( "#define yytext_ptr yytext" );
+
+ if ( interactive )
+ outn( "#define YY_INTERACTIVE" );
+ }
+
+ else
+ {
+ if ( do_stdinit )
+ {
+ outn( "#ifdef VMS" );
+ outn( "#ifndef __VMS_POSIX" );
+ outn( yy_nostdinit );
+ outn( "#else" );
+ outn( yy_stdinit );
+ outn( "#endif" );
+ outn( "#else" );
+ outn( yy_stdinit );
+ outn( "#endif" );
+ }
+
+ else
+ outn( yy_nostdinit );
+ }
+
+ if ( fullspd )
+ outn( "typedef yyconst struct yy_trans_info *yy_state_type;" );
+ else if ( ! C_plus_plus )
+ outn( "typedef int yy_state_type;" );
+
+ if ( ddebug )
+ outn( "\n#define FLEX_DEBUG" );
+
+ if ( lex_compat )
+ outn( "#define YY_FLEX_LEX_COMPAT" );
+
+ if ( do_yylineno && ! C_plus_plus )
+ {
+ outn( "extern int yylineno;" );
+ outn( "int yylineno = 1;" );
+ }
+
+ if ( C_plus_plus )
+ {
+ outn( "\n#include <FlexLexer.h>" );
+
+ if ( yyclass )
+ {
+ outn( "int yyFlexLexer::yylex()" );
+ outn( "\t{" );
+ outn(
+"\tLexerError( \"yyFlexLexer::yylex invoked but %option yyclass used\" );" );
+ outn( "\treturn 0;" );
+ outn( "\t}" );
+
+ out_str( "\n#define YY_DECL int %s::yylex()\n",
+ yyclass );
+ }
+ }
+
+ else
+ {
+ if ( yytext_is_array )
+ outn( "extern char yytext[];\n" );
+
+ else
+ {
+ outn( "extern char *yytext;" );
+ outn( "#define yytext_ptr yytext" );
+ }
+
+ if ( yyclass )
+ flexerror(
+ _( "%option yyclass only meaningful for C++ scanners" ) );
+ }
+
+ 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 );
+
+ 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()
+ {
+ FILE *f = stdout;
+
+ fprintf( f,
+_( "%s [-bcdfhilnpstvwBFILTV78+? -C[aefFmr] -ooutput -Pprefix -Sskeleton]\n" ),
+ program_name );
+ fprintf( f, _( "\t[--help --version] [file ...]\n" ) );
+
+ fprintf( f, _( "\t-b generate backing-up information to %s\n" ),
+ backing_name );
+ fprintf( f, _( "\t-c do-nothing POSIX option\n" ) );
+ fprintf( f, _( "\t-d turn on debug mode in generated scanner\n" ) );
+ fprintf( f, _( "\t-f generate fast, large scanner\n" ) );
+ fprintf( f, _( "\t-h produce this help message\n" ) );
+ fprintf( f, _( "\t-i generate case-insensitive scanner\n" ) );
+ fprintf( f, _( "\t-l maximal compatibility with original lex\n" ) );
+ fprintf( f, _( "\t-n do-nothing POSIX option\n" ) );
+ fprintf( f, _( "\t-p generate performance report to stderr\n" ) );
+ fprintf( f,
+ _( "\t-s suppress default rule to ECHO unmatched text\n" ) );
+
+ if ( ! did_outfilename )
+ {
+ sprintf( outfile_path, outfile_template,
+ prefix, C_plus_plus ? "cc" : "c" );
+ outfilename = outfile_path;
+ }
+
+ fprintf( f,
+ _( "\t-t write generated scanner on stdout instead of %s\n" ),
+ outfilename );
+
+ fprintf( f,
+ _( "\t-v write summary of scanner statistics to f\n" ) );
+ fprintf( f, _( "\t-w do not generate warnings\n" ) );
+ fprintf( f, _( "\t-B generate batch scanner (opposite of -I)\n" ) );
+ fprintf( f,
+ _( "\t-F use alternative fast scanner representation\n" ) );
+ fprintf( f,
+ _( "\t-I generate interactive scanner (opposite of -B)\n" ) );
+ fprintf( f, _( "\t-L suppress #line directives in scanner\n" ) );
+ fprintf( f, _( "\t-T %s should run in trace mode\n" ), program_name );
+ fprintf( f, _( "\t-V report %s version\n" ), program_name );
+ fprintf( f, _( "\t-7 generate 7-bit scanner\n" ) );
+ fprintf( f, _( "\t-8 generate 8-bit scanner\n" ) );
+ fprintf( f, _( "\t-+ generate C++ scanner class\n" ) );
+ fprintf( f, _( "\t-? produce this help message\n" ) );
+ fprintf( f,
+_( "\t-C specify degree of table compression (default is -Cem):\n" ) );
+ fprintf( f,
+_( "\t\t-Ca trade off larger tables for better memory alignment\n" ) );
+ fprintf( f, _( "\t\t-Ce construct equivalence classes\n" ) );
+ fprintf( f,
+_( "\t\t-Cf do not compress scanner tables; use -f representation\n" ) );
+ fprintf( f,
+_( "\t\t-CF do not compress scanner tables; use -F representation\n" ) );
+ fprintf( f, _( "\t\t-Cm construct meta-equivalence classes\n" ) );
+ fprintf( f,
+ _( "\t\t-Cr use read() instead of stdio for scanner input\n" ) );
+ fprintf( f, _( "\t-o specify output filename\n" ) );
+ fprintf( f, _( "\t-P specify scanner prefix other than \"yy\"\n" ) );
+ fprintf( f, _( "\t-S specify skeleton file\n" ) );
+ fprintf( f, _( "\t--help produce this help message\n" ) );
+ fprintf( f, _( "\t--version report %s version\n" ), program_name );
+ }
diff --git a/usr.bin/lex/misc.c b/usr.bin/lex/misc.c
new file mode 100644
index 0000000..34c67c5
--- /dev/null
+++ b/usr.bin/lex/misc.c
@@ -0,0 +1,886 @@
+/* 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.2 1996/06/19 20:26:19 nate Exp $ */
+
+#include "flexdef.h"
+
+
+void action_define( defname, value )
+char *defname;
+int value;
+ {
+ char buf[MAXLINE];
+
+ if ( (int) strlen( defname ) > MAXLINE / 2 )
+ {
+ format_pinpoint_message( _( "name \"%s\" ridiculously long" ),
+ defname );
+ return;
+ }
+
+ sprintf( buf, "#define %s %d\n", defname, value );
+ add_action( buf );
+ }
+
+
+void add_action( new_text )
+char *new_text;
+ {
+ int len = strlen( new_text );
+
+ while ( len + action_index >= action_size - 10 /* slop */ )
+ {
+ int new_size = action_size * 2;
+
+ if ( new_size <= 0 )
+ /* Increase just a little, to try to avoid overflow
+ * on 16-bit machines.
+ */
+ action_size += action_size / 8;
+ else
+ action_size = new_size;
+
+ 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;
+size_t element_size;
+ {
+ register void *mem;
+ size_t num_bytes = element_size * size;
+
+ mem = flex_alloc( num_bytes );
+ if ( ! mem )
+ 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 const char *str;
+ {
+ register const char *c1;
+ register char *c2;
+ char *copy;
+ unsigned int size;
+
+ /* find length */
+ for ( c1 = str; *c1; ++c1 )
+ ;
+
+ size = (c1 - str + 1) * sizeof( char );
+ copy = (char *) flex_alloc( size );
+
+ if ( copy == NULL )
+ flexfatal( _( "dynamic memory failure in copy_string()" ) );
+
+ for ( c2 = copy; (*c2++ = *str++) != 0; )
+ ;
+
+ 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++) != 0; )
+ ;
+
+ 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 */
+ outn( " } ;\n" );
+
+ dataline = 0;
+ datapos = 0;
+ }
+
+
+/* dataflush - flush generated data statements */
+
+void dataflush()
+ {
+ outc( '\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.
+ */
+ outc( '\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 )
+const char msg[];
+ {
+ fprintf( stderr, "%s: %s\n", program_name, msg );
+ flexend( 1 );
+ }
+
+
+/* flexfatal - report a fatal error message and terminate */
+
+void flexfatal( msg )
+const char msg[];
+ {
+ fprintf( stderr, _( "%s: fatal internal error, %s\n" ),
+ program_name, msg );
+ exit( 1 );
+ }
+
+
+/* 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;
+ }
+
+
+/* lerrif - report an error message formatted with one integer argument */
+
+void lerrif( msg, arg )
+const 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 )
+const char msg[], arg[];
+ {
+ char errmsg[MAXLINE];
+
+ (void) sprintf( errmsg, msg, arg );
+ flexerror( errmsg );
+ }
+
+
+/* line_directive_out - spit out a "#line" statement */
+
+void line_directive_out( output_file, do_infile )
+FILE *output_file;
+int do_infile;
+ {
+ char directive[MAXLINE], filename[MAXLINE];
+ char *s1, *s2, *s3;
+ static char line_fmt[] = "#line %d \"%s\"\n";
+
+ if ( ! gen_line_dirs )
+ return;
+
+ if ( (do_infile && ! infilename) || (! do_infile && ! outfilename) )
+ /* don't know the filename to use, skip */
+ return;
+
+ s1 = do_infile ? infilename : outfilename;
+ s2 = filename;
+ s3 = &filename[sizeof( filename ) - 2];
+
+ while ( s2 < s3 && *s1 )
+ {
+ if ( *s1 == '\\' )
+ /* Escape the '\' */
+ *s2++ = '\\';
+
+ *s2++ = *s1++;
+ }
+
+ *s2 = '\0';
+
+ if ( do_infile )
+ sprintf( directive, line_fmt, linenum, filename );
+ else
+ {
+ if ( output_file == stdout )
+ /* Account for the line directive itself. */
+ ++out_linenum;
+
+ sprintf( directive, line_fmt, out_linenum, filename );
+ }
+
+ /* 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 )
+ {
+ outc( ',' );
+ dataflush();
+ }
+
+ if ( datapos == 0 )
+ /* Indent. */
+ out( " " );
+
+ else
+ outc( ',' );
+
+ ++datapos;
+
+ out_dec( "%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 )
+ {
+ outc( ',' );
+ dataflush();
+ }
+
+ if ( datapos == 0 )
+ /* Indent. */
+ out( " " );
+ else
+ outc( ',' );
+
+ ++datapos;
+
+ out_dec( "%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';
+
+#if __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':
+ { /* \<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] ) &&
+ isxdigit( (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;
+ }
+
+
+/* out - various flavors of outputing a (possibly formatted) string for the
+ * generated scanner, keeping track of the line count.
+ */
+
+void out( str )
+const char str[];
+ {
+ fputs( str, stdout );
+ out_line_count( str );
+ }
+
+void out_dec( fmt, n )
+const char fmt[];
+int n;
+ {
+ printf( fmt, n );
+ out_line_count( fmt );
+ }
+
+void out_dec2( fmt, n1, n2 )
+const char fmt[];
+int n1, n2;
+ {
+ printf( fmt, n1, n2 );
+ out_line_count( fmt );
+ }
+
+void out_hex( fmt, x )
+const char fmt[];
+unsigned int x;
+ {
+ printf( fmt, x );
+ out_line_count( fmt );
+ }
+
+void out_line_count( str )
+const char str[];
+ {
+ register int i;
+
+ for ( i = 0; str[i]; ++i )
+ if ( str[i] == '\n' )
+ ++out_linenum;
+ }
+
+void out_str( fmt, str )
+const char fmt[], str[];
+ {
+ printf( fmt, str );
+ out_line_count( fmt );
+ out_line_count( str );
+ }
+
+void out_str3( fmt, s1, s2, s3 )
+const char fmt[], s1[], s2[], s3[];
+ {
+ printf( fmt, s1, s2, s3 );
+ out_line_count( fmt );
+ out_line_count( s1 );
+ out_line_count( s2 );
+ out_line_count( s3 );
+ }
+
+void out_str_dec( fmt, str, n )
+const char fmt[], str[];
+int n;
+ {
+ printf( fmt, str, n );
+ out_line_count( fmt );
+ out_line_count( str );
+ }
+
+void outc( c )
+int c;
+ {
+ putc( c, stdout );
+
+ if ( c == '\n' )
+ ++out_linenum;
+ }
+
+void outn( str )
+const char str[];
+ {
+ puts( str );
+ out_line_count( str );
+ ++out_linenum;
+ }
+
+
+/* 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";
+
+#if __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;
+size_t element_size;
+ {
+ register void *new_array;
+ size_t num_bytes = element_size * size;
+
+ new_array = flex_realloc( array, num_bytes );
+ if ( ! new_array )
+ 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 = (char *) 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.
+ */
+ out( buf );
+ else
+ outn( 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;
+ {
+ out_dec2( " {%4d,%4d },", element_v, element_n );
+
+ datapos += TRANS_STRUCT_PRINT_LENGTH;
+
+ if ( datapos >= 79 - TRANS_STRUCT_PRINT_LENGTH )
+ {
+ outc( '\n' );
+
+ if ( ++dataline % 10 == 0 )
+ outc( '\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_t) 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;
+size_t 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..a03a11a
--- /dev/null
+++ b/usr.bin/lex/mkskel.sh
@@ -0,0 +1,16 @@
+#! /bin/sh
+
+cat <<!
+/* File created from flex.skl via mkskel.sh */
+
+#include "flexdef.h"
+
+const 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..5fbec9a
--- /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.2 1996/06/19 20:26:24 nate 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_RULE_SETUP's.
+ */
+ if ( ! continued_action )
+ add_action( "YY_RULE_SETUP\n" );
+
+ line_directive_out( (FILE *) 0, 1 );
+ }
+
+
+/* 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..8dbd125
--- /dev/null
+++ b/usr.bin/lex/parse.y
@@ -0,0 +1,913 @@
+/* parse.y - parser for flex input */
+
+%token CHAR NUMBER SECTEND SCDECL XSCDECL NAME PREVCCL EOF_OP
+%token OPTION_OP OPT_OUTFILE OPT_PREFIX OPT_YYCLASS
+
+%token CCE_ALNUM CCE_ALPHA CCE_BLANK CCE_CNTRL CCE_DIGIT CCE_GRAPH
+%token CCE_LOWER CCE_PRINT CCE_PUNCT CCE_SPACE CCE_UPPER CCE_XDIGIT
+
+%{
+/*-
+ * 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.28 95/04/21 11:51:51 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. What a piece. */
+# ifdef _AIX
+ #pragma alloca
+# endif
+#endif
+
+#include "flexdef.h"
+
+/* The remainder of the alloca() cruft has to come after including flexdef.h,
+ * so HAVE_ALLOCA_H is (possibly) defined.
+ */
+#ifdef YYBISON
+# ifdef __GNUC__
+# ifndef alloca
+# define alloca __builtin_alloca
+# endif
+# else
+# if HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef __hpux
+void *alloca ();
+# else
+# ifdef __TURBOC__
+# include <malloc.h>
+# else
+char *alloca ();
+# endif
+# endif
+# endif
+# endif
+#endif
+
+/* Bletch, ^^^^ that was ugly! */
+
+
+int pat, scnum, eps, headcnt, trailcnt, anyccl, lastchar, i, rulelen;
+int trlcontxt, xcluflg, currccl, cclsorted, varlength, variable_trail_rule;
+
+int *scon_stk;
+int scon_stk_ptr;
+
+static int madeany = false; /* whether we've made the '.' character class */
+int previous_continued_action; /* whether the previous rule's action was '|' */
+
+/* Expand a POSIX character class expression. */
+#define CCL_EXPR(func) \
+ { \
+ int c; \
+ for ( c = 0; c < csize; ++c ) \
+ if ( isascii(c) && func(c) ) \
+ ccladd( currccl, c ); \
+ }
+
+/* While POSIX defines isblank(), it's not ANSI C. */
+#define IS_BLANK(c) ((c) == ' ' || (c) == '\t')
+
+/* 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 );
+ }
+ ;
+
+sect1 : sect1 startconddecl namelist1
+ | sect1 options
+ |
+ | error
+ { synerr( "unknown error processing section 1" ); }
+ ;
+
+sect1end : SECTEND
+ {
+ check_options();
+ scon_stk = allocate_integer_array( lastsc + 1 );
+ scon_stk_ptr = 0;
+ }
+ ;
+
+startconddecl : SCDECL
+ { xcluflg = false; }
+
+ | XSCDECL
+ { xcluflg = true; }
+ ;
+
+namelist1 : namelist1 NAME
+ { scinstal( nmstr, xcluflg ); }
+
+ | NAME
+ { scinstal( nmstr, xcluflg ); }
+
+ | error
+ { synerr( "bad start condition list" ); }
+ ;
+
+options : OPTION_OP optionlist
+ ;
+
+optionlist : optionlist option
+ |
+ ;
+
+option : OPT_OUTFILE '=' NAME
+ {
+ outfilename = copy_string( nmstr );
+ did_outfilename = 1;
+ }
+ | OPT_PREFIX '=' NAME
+ { prefix = copy_string( nmstr ); }
+ | OPT_YYCLASS '=' NAME
+ { yyclass = copy_string( nmstr ); }
+ ;
+
+sect2 : sect2 scon initforrule flexrule '\n'
+ { scon_stk_ptr = $2; }
+ | sect2 scon '{' sect2 '}'
+ { scon_stk_ptr = $2; }
+ |
+ ;
+
+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;
+ in_rule = true;
+
+ new_rule();
+ }
+ ;
+
+flexrule : '^' rule
+ {
+ pat = $2;
+ finish_rule( pat, variable_trail_rule,
+ headcnt, trailcnt );
+
+ if ( scon_stk_ptr > 0 )
+ {
+ for ( i = 1; i <= scon_stk_ptr; ++i )
+ scbol[scon_stk[i]] =
+ mkbranch( scbol[scon_stk[i]],
+ pat );
+ }
+
+ else
+ {
+ /* 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 );
+
+ if ( scon_stk_ptr > 0 )
+ {
+ for ( i = 1; i <= scon_stk_ptr; ++i )
+ scset[scon_stk[i]] =
+ mkbranch( scset[scon_stk[i]],
+ pat );
+ }
+
+ else
+ {
+ for ( i = 1; i <= lastsc; ++i )
+ if ( ! scxclu[i] )
+ scset[i] =
+ mkbranch( scset[i],
+ pat );
+ }
+ }
+
+ | EOF_OP
+ {
+ if ( scon_stk_ptr > 0 )
+ build_eof_action();
+
+ else
+ {
+ /* This EOF applies to all start conditions
+ * which don't already have EOF actions.
+ */
+ for ( i = 1; i <= lastsc; ++i )
+ if ( ! sceof[i] )
+ scon_stk[++scon_stk_ptr] = i;
+
+ if ( scon_stk_ptr == 0 )
+ warn(
+ "all start conditions already have <<EOF>> rules" );
+
+ else
+ build_eof_action();
+ }
+ }
+
+ | error
+ { synerr( "unrecognized rule" ); }
+ ;
+
+scon_stk_ptr :
+ { $$ = scon_stk_ptr; }
+ ;
+
+scon : '<' scon_stk_ptr namelist2 '>'
+ { $$ = $2; }
+
+ | '<' '*' '>'
+ {
+ $$ = scon_stk_ptr;
+
+ for ( i = 1; i <= lastsc; ++i )
+ {
+ int j;
+
+ for ( j = 1; j <= scon_stk_ptr; ++j )
+ if ( scon_stk[j] == i )
+ break;
+
+ if ( j > scon_stk_ptr )
+ scon_stk[++scon_stk_ptr] = i;
+ }
+ }
+
+ |
+ { $$ = scon_stk_ptr; }
+ ;
+
+namelist2 : namelist2 ',' sconname
+
+ | sconname
+
+ | error
+ { synerr( "bad start condition list" ); }
+ ;
+
+sconname : NAME
+ {
+ if ( (scnum = sclookup( nmstr )) == 0 )
+ format_pinpoint_message(
+ "undeclared start condition %s",
+ nmstr );
+ else
+ {
+ for ( i = 1; i <= scon_stk_ptr; ++i )
+ if ( scon_stk[i] == scnum )
+ {
+ format_warn(
+ "<%s> specified twice",
+ scname[scnum] );
+ break;
+ }
+
+ if ( i > scon_stk_ptr )
+ scon_stk[++scon_stk_ptr] = 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;
+ }
+
+ | ccl ccl_expr
+ {
+ /* Too hard to properly maintain cclsorted. */
+ cclsorted = false;
+ $$ = $1;
+ }
+
+ |
+ {
+ cclsorted = true;
+ lastchar = 0;
+ currccl = $$ = cclinit();
+ }
+ ;
+
+ccl_expr: CCE_ALNUM { CCL_EXPR(isalnum) }
+ | CCE_ALPHA { CCL_EXPR(isalpha) }
+ | CCE_BLANK { CCL_EXPR(IS_BLANK) }
+ | CCE_CNTRL { CCL_EXPR(iscntrl) }
+ | CCE_DIGIT { CCL_EXPR(isdigit) }
+ | CCE_GRAPH { CCL_EXPR(isgraph) }
+ | CCE_LOWER { CCL_EXPR(islower) }
+ | CCE_PRINT { CCL_EXPR(isprint) }
+ | CCE_PUNCT { CCL_EXPR(ispunct) }
+ | CCE_SPACE { CCL_EXPR(isspace) }
+ | CCE_UPPER {
+ if ( caseins )
+ CCL_EXPR(islower)
+ else
+ CCL_EXPR(isupper)
+ }
+ | CCE_XDIGIT { CCL_EXPR(isxdigit) }
+ ;
+
+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 <= scon_stk_ptr; ++i )
+ {
+ if ( sceof[scon_stk[i]] )
+ format_pinpoint_message(
+ "multiple <<EOF>> rules for start condition %s",
+ scname[scon_stk[i]] );
+
+ else
+ {
+ sceof[scon_stk[i]] = true;
+ sprintf( action_text, "case YY_STATE_EOF(%s):\n",
+ scname[scon_stk[i]] );
+ add_action( action_text );
+ }
+ }
+
+ line_directive_out( (FILE *) 0, 1 );
+
+ /* 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 );
+ }
+
+
+/* format_warn - write out formatted warning */
+
+void format_warn( msg, arg )
+char msg[], arg[];
+ {
+ char warn_msg[MAXLINE];
+
+ (void) sprintf( warn_msg, msg, arg );
+ warn( warn_msg );
+ }
+
+
+/* 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..2db8d78
--- /dev/null
+++ b/usr.bin/lex/scan.l
@@ -0,0 +1,710 @@
+/* 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: /home/ncvs/src/usr.bin/lex/scan.l,v 1.2 1996/06/19 22:25:32 nate Exp $ */
+
+#include "flexdef.h"
+#include "parse.h"
+
+#define ACTION_ECHO add_action( yytext )
+#define ACTION_IFDEF(def, should_define) \
+ { \
+ if ( should_define ) \
+ action_define( def, 1 ); \
+ }
+
+#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;
+%}
+
+%option caseless nodefault outfile="scan.c" stack noyy_top_state
+%option nostdinit
+
+%x SECT2 SECT2PROLOG SECT3 CODEBLOCK PICKUPDEF SC CARETISBOL NUM QUOTE
+%x FIRSTCCL CCL ACTION RECOVER COMMENT ACTION_STRING PERCENT_BRACE_ACTION
+%x OPTION LINEDIR
+
+WS [[:blank:]]+
+OPTWS [[:blank:]]*
+NOT_WS [^[:blank:]\n]
+
+NL \r?\n
+
+NAME ([[:alpha:]_][[:alnum:]_-]*)
+NOT_NAME [^[:alpha:]_*\n]+
+
+SCNAME {NAME}
+
+ESCSEQ (\\([^\n]|[0-7]{1,3}|x[[:xdigit:]]{1,2}))
+
+FIRST_CCL_CHAR ([^\\\n]|{ESCSEQ})
+CCL_CHAR ([^\\\n\]]|{ESCSEQ})
+CCL_EXPR ("[:"[[:alpha:]]+":]")
+
+LEXOPT [aceknopr]
+
+%%
+ static int bracelevel, didadef, indented_code;
+ static int doing_rule_action = false;
+ static int option_sense;
+
+ int doing_codeblock = false;
+ int i;
+ Char nmdef[MAXLINE], myesc();
+
+
+<INITIAL>{
+ ^{WS} indented_code = true; BEGIN(CODEBLOCK);
+ ^"/*" ACTION_ECHO; yy_push_state( COMMENT );
+ ^#{OPTWS}line{WS} yy_push_state( LINEDIR );
+ ^"%s"{NAME}? return SCDECL;
+ ^"%x"{NAME}? return XSCDECL;
+ ^"%{".*{NL} {
+ ++linenum;
+ line_directive_out( (FILE *) 0, 1 );
+ indented_code = false;
+ BEGIN(CODEBLOCK);
+ }
+
+ {WS} /* discard */
+
+ ^"%%".* {
+ sectnum = 2;
+ bracelevel = 0;
+ mark_defs1();
+ line_directive_out( (FILE *) 0, 1 );
+ BEGIN(SECT2PROLOG);
+ return SECTEND;
+ }
+
+ ^"%pointer".*{NL} yytext_is_array = false; ++linenum;
+ ^"%array".*{NL} yytext_is_array = true; ++linenum;
+
+ ^"%option" BEGIN(OPTION); return OPTION_OP;
+
+ ^"%"{LEXOPT}{OPTWS}[[:digit:]]*{OPTWS}{NL} ++linenum; /* ignore */
+ ^"%"{LEXOPT}{WS}.*{NL} ++linenum; /* ignore */
+
+ ^"%"[^sxaceknopr{}].* synerr( _( "unrecognized '%' directive" ) );
+
+ ^{NAME} {
+ strcpy( nmstr, yytext );
+ didadef = false;
+ BEGIN(PICKUPDEF);
+ }
+
+ {SCNAME} RETURNNAME;
+ ^{OPTWS}{NL} ++linenum; /* allows blank lines in section 1 */
+ {OPTWS}{NL} ACTION_ECHO; ++linenum; /* maybe end of comment line */
+}
+
+
+<COMMENT>{
+ "*/" ACTION_ECHO; yy_pop_state();
+ "*" ACTION_ECHO;
+ [^*\n]+ ACTION_ECHO;
+ [^*\n]*{NL} ++linenum; ACTION_ECHO;
+}
+
+<LINEDIR>{
+ \n yy_pop_state();
+ [[:digit:]]+ linenum = myctoi( yytext );
+
+ \"[^"\n]*\" {
+ flex_free( (void *) infilename );
+ infilename = copy_string( yytext + 1 );
+ infilename[strlen( infilename ) - 1] = '\0';
+ }
+ . /* ignore spurious characters */
+}
+
+<CODEBLOCK>{
+ ^"%}".*{NL} ++linenum; BEGIN(INITIAL);
+
+ {NAME}|{NOT_NAME}|. ACTION_ECHO;
+
+ {NL} {
+ ++linenum;
+ ACTION_ECHO;
+ if ( indented_code )
+ BEGIN(INITIAL);
+ }
+}
+
+
+<PICKUPDEF>{
+ {WS} /* separates name and definition */
+
+ {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;
+ }
+
+ {NL} {
+ if ( ! didadef )
+ synerr( _( "incomplete name definition" ) );
+ BEGIN(INITIAL);
+ ++linenum;
+ }
+}
+
+
+<OPTION>{
+ {NL} ++linenum; BEGIN(INITIAL);
+ {WS} option_sense = true;
+
+ "=" return '=';
+
+ no option_sense = ! option_sense;
+
+ 7bit csize = option_sense ? 128 : 256;
+ 8bit csize = option_sense ? 256 : 128;
+
+ align long_align = option_sense;
+ always-interactive {
+ action_define( "YY_ALWAYS_INTERACTIVE", option_sense );
+ }
+ array yytext_is_array = option_sense;
+ backup backing_up_report = option_sense;
+ batch interactive = ! option_sense;
+ "c++" C_plus_plus = option_sense;
+ caseful|case-sensitive caseins = ! option_sense;
+ caseless|case-insensitive caseins = option_sense;
+ debug ddebug = option_sense;
+ default spprdflt = ! option_sense;
+ ecs useecs = option_sense;
+ fast {
+ useecs = usemecs = false;
+ use_read = fullspd = true;
+ }
+ full {
+ useecs = usemecs = false;
+ use_read = fulltbl = true;
+ }
+ input ACTION_IFDEF("YY_NO_INPUT", ! option_sense);
+ interactive interactive = option_sense;
+ lex-compat lex_compat = option_sense;
+ main {
+ action_define( "YY_MAIN", option_sense );
+ do_yywrap = ! option_sense;
+ }
+ meta-ecs usemecs = option_sense;
+ never-interactive {
+ action_define( "YY_NEVER_INTERACTIVE", option_sense );
+ }
+ perf-report performance_report += option_sense ? 1 : -1;
+ pointer yytext_is_array = ! option_sense;
+ read use_read = option_sense;
+ reject reject_really_used = option_sense;
+ stack action_define( "YY_STACK_USED", option_sense );
+ stdinit do_stdinit = option_sense;
+ stdout use_stdout = option_sense;
+ unput ACTION_IFDEF("YY_NO_UNPUT", ! option_sense);
+ verbose printstats = option_sense;
+ warn nowarn = ! option_sense;
+ yylineno do_yylineno = option_sense;
+ yymore yymore_really_used = option_sense;
+ yywrap do_yywrap = option_sense;
+
+ yy_push_state ACTION_IFDEF("YY_NO_PUSH_STATE", ! option_sense);
+ yy_pop_state ACTION_IFDEF("YY_NO_POP_STATE", ! option_sense);
+ yy_top_state ACTION_IFDEF("YY_NO_TOP_STATE", ! option_sense);
+
+ yy_scan_buffer ACTION_IFDEF("YY_NO_SCAN_BUFFER", ! option_sense);
+ yy_scan_bytes ACTION_IFDEF("YY_NO_SCAN_BYTES", ! option_sense);
+ yy_scan_string ACTION_IFDEF("YY_NO_SCAN_STRING", ! option_sense);
+
+ outfile return OPT_OUTFILE;
+ prefix return OPT_PREFIX;
+ yyclass return OPT_YYCLASS;
+
+ \"[^"\n]*\" {
+ strcpy( nmstr, yytext + 1 );
+ nmstr[strlen( nmstr ) - 1] = '\0';
+ return NAME;
+ }
+
+ (([a-mo-z]|n[a-np-z])[[:alpha:]\-+]*)|. {
+ format_synerr( _( "unrecognized %%option: %s" ),
+ yytext );
+ BEGIN(RECOVER);
+ }
+}
+
+<RECOVER>.*{NL} ++linenum; BEGIN(INITIAL);
+
+
+<SECT2PROLOG>{
+ ^"%{".* ++bracelevel; yyless( 2 ); /* eat only %{ */
+ ^"%}".* --bracelevel; yyless( 2 ); /* eat only %} */
+
+ ^{WS}.* ACTION_ECHO; /* indented code in prolog */
+
+ ^{NOT_WS}.* { /* non-indented code */
+ if ( bracelevel <= 0 )
+ { /* not in %{ ... %} */
+ yyless( 0 ); /* put it all back */
+ yy_set_bol( 1 );
+ mark_prolog();
+ BEGIN(SECT2);
+ }
+ else
+ ACTION_ECHO;
+ }
+
+ .* ACTION_ECHO;
+ {NL} ++linenum; ACTION_ECHO;
+
+ <<EOF>> {
+ mark_prolog();
+ sectnum = 0;
+ yyterminate(); /* to stop the parser */
+ }
+}
+
+<SECT2>{
+ ^{OPTWS}{NL} ++linenum; /* allow blank lines in section 2 */
+
+ ^{OPTWS}"%{" {
+ indented_code = false;
+ doing_codeblock = true;
+ bracelevel = 1;
+ BEGIN(PERCENT_BRACE_ACTION);
+ }
+
+ ^{OPTWS}"<" BEGIN(SC); return '<';
+ ^{OPTWS}"^" return '^';
+ \" BEGIN(QUOTE); return '"';
+ "{"/[[:digit:]] BEGIN(NUM); return '{';
+ "$"/([[:blank:]]|{NL}) return '$';
+
+ {WS}"%{" {
+ bracelevel = 1;
+ BEGIN(PERCENT_BRACE_ACTION);
+
+ if ( in_rule )
+ {
+ doing_rule_action = true;
+ in_rule = false;
+ return '\n';
+ }
+ }
+ {WS}"|".*{NL} continued_action = true; ++linenum; return '\n';
+
+ ^{WS}"/*" {
+ yyless( yyleng - 2 ); /* put back '/', '*' */
+ bracelevel = 0;
+ continued_action = false;
+ BEGIN(ACTION);
+ }
+
+ ^{WS} /* allow indented rules */
+
+ {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);
+
+ if ( in_rule )
+ {
+ doing_rule_action = true;
+ in_rule = false;
+ return '\n';
+ }
+ }
+
+ {OPTWS}{NL} {
+ bracelevel = 0;
+ continued_action = false;
+ BEGIN(ACTION);
+ unput( '\n' ); /* so <ACTION> sees it */
+
+ if ( in_rule )
+ {
+ doing_rule_action = true;
+ in_rule = false;
+ return '\n';
+ }
+ }
+
+ ^{OPTWS}"<<EOF>>" |
+ "<<EOF>>" return EOF_OP;
+
+ ^"%%".* {
+ sectnum = 3;
+ BEGIN(SECT3);
+ yyterminate(); /* to stop the parser */
+ }
+
+ "["({FIRST_CCL_CHAR}|{CCL_EXPR})({CCL_CHAR}|{CCL_EXPR})* {
+ int cclval;
+
+ strcpy( nmstr, yytext );
+
+ /* Check to see if we've already encountered this
+ * ccl.
+ */
+ if ( (cclval = ccllookup( (Char *) nmstr )) != 0 )
+ {
+ 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 '[';
+ }
+ }
+
+ "{"{NAME}"}" {
+ register Char *nmdefptr;
+ Char *ndlookup();
+
+ strcpy( nmstr, yytext + 1 );
+ nmstr[yyleng - 2] = '\0'; /* chop trailing brace */
+
+ if ( (nmdefptr = ndlookup( nmstr )) == 0 )
+ 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('(');
+ }
+ }
+ }
+
+ [/|*+?.(){}] return (unsigned char) yytext[0];
+ . RETURNCHAR;
+}
+
+
+<SC>{
+ [,*] return (unsigned char) yytext[0];
+ ">" BEGIN(SECT2); return '>';
+ ">"/^ BEGIN(CARETISBOL); return '>';
+ {SCNAME} RETURNNAME;
+ . {
+ format_synerr( _( "bad <start condition>: %s" ),
+ yytext );
+ }
+}
+
+<CARETISBOL>"^" BEGIN(SECT2); return '^';
+
+
+<QUOTE>{
+ [^"\n] RETURNCHAR;
+ \" BEGIN(SECT2); return '"';
+
+ {NL} {
+ synerr( _( "missing quote" ) );
+ BEGIN(SECT2);
+ ++linenum;
+ return '"';
+ }
+}
+
+
+<FIRSTCCL>{
+ "^"/[^-\]\n] BEGIN(CCL); return '^';
+ "^"/("-"|"]") return '^';
+ . BEGIN(CCL); RETURNCHAR;
+}
+
+<CCL>{
+ -/[^\]\n] return '-';
+ [^\]\n] RETURNCHAR;
+ "]" BEGIN(SECT2); return ']';
+ .|{NL} {
+ synerr( _( "bad character class" ) );
+ BEGIN(SECT2);
+ return ']';
+ }
+}
+
+<FIRSTCCL,CCL>{
+ "[:alnum:]" BEGIN(CCL); return CCE_ALNUM;
+ "[:alpha:]" BEGIN(CCL); return CCE_ALPHA;
+ "[:blank:]" BEGIN(CCL); return CCE_BLANK;
+ "[:cntrl:]" BEGIN(CCL); return CCE_CNTRL;
+ "[:digit:]" BEGIN(CCL); return CCE_DIGIT;
+ "[:graph:]" BEGIN(CCL); return CCE_GRAPH;
+ "[:lower:]" BEGIN(CCL); return CCE_LOWER;
+ "[:print:]" BEGIN(CCL); return CCE_PRINT;
+ "[:punct:]" BEGIN(CCL); return CCE_PUNCT;
+ "[:space:]" BEGIN(CCL); return CCE_SPACE;
+ "[:upper:]" BEGIN(CCL); return CCE_UPPER;
+ "[:xdigit:]" BEGIN(CCL); return CCE_XDIGIT;
+ {CCL_EXPR} {
+ format_synerr(
+ _( "bad character class expression: %s" ),
+ yytext );
+ BEGIN(CCL); return CCE_ALNUM;
+ }
+}
+
+<NUM>{
+ [[:digit:]]+ {
+ yylval = myctoi( yytext );
+ return NUMBER;
+ }
+
+ "," return ',';
+ "}" BEGIN(SECT2); return '}';
+
+ . {
+ synerr( _( "bad character inside {}'s" ) );
+ BEGIN(SECT2);
+ return '}';
+ }
+
+ {NL} {
+ synerr( _( "missing }" ) );
+ BEGIN(SECT2);
+ ++linenum;
+ return '}';
+ }
+}
+
+
+<PERCENT_BRACE_ACTION>{
+ {OPTWS}"%}".* bracelevel = 0;
+
+ <ACTION>"/*" ACTION_ECHO; yy_push_state( COMMENT );
+
+ <CODEBLOCK,ACTION>{
+ "reject" {
+ ACTION_ECHO;
+ CHECK_REJECT(yytext);
+ }
+ "yymore" {
+ ACTION_ECHO;
+ CHECK_YYMORE(yytext);
+ }
+ }
+
+ {NAME}|{NOT_NAME}|. ACTION_ECHO;
+ {NL} {
+ ++linenum;
+ ACTION_ECHO;
+ if ( bracelevel == 0 ||
+ (doing_codeblock && indented_code) )
+ {
+ if ( doing_rule_action )
+ add_action( "\tYY_BREAK\n" );
+
+ doing_rule_action = doing_codeblock = false;
+ BEGIN(SECT2);
+ }
+ }
+}
+
+
+ /* Reject and YYmore() are checked for above, in PERCENT_BRACE_ACTION */
+<ACTION>{
+ "{" ACTION_ECHO; ++bracelevel;
+ "}" ACTION_ECHO; --bracelevel;
+ [^[:alpha:]_{}"'/\n]+ ACTION_ECHO;
+ {NAME} ACTION_ECHO;
+ "'"([^'\\\n]|\\.)*"'" ACTION_ECHO; /* character constant */
+ \" ACTION_ECHO; BEGIN(ACTION_STRING);
+ {NL} {
+ ++linenum;
+ ACTION_ECHO;
+ if ( bracelevel == 0 )
+ {
+ if ( doing_rule_action )
+ add_action( "\tYY_BREAK\n" );
+
+ doing_rule_action = false;
+ BEGIN(SECT2);
+ }
+ }
+ . ACTION_ECHO;
+}
+
+<ACTION_STRING>{
+ [^"\\\n]+ ACTION_ECHO;
+ \\. ACTION_ECHO;
+ {NL} ++linenum; ACTION_ECHO;
+ \" ACTION_ECHO; BEGIN(ACTION);
+ . ACTION_ECHO;
+}
+
+<COMMENT,ACTION,ACTION_STRING><<EOF>> {
+ synerr( _( "EOF encountered inside an action" ) );
+ yyterminate();
+ }
+
+
+<SECT2,QUOTE,FIRSTCCL,CCL>{ESCSEQ} {
+ yylval = myesc( (Char *) yytext );
+
+ if ( YY_START == FIRSTCCL )
+ BEGIN(CCL);
+
+ return CHAR;
+ }
+
+
+<SECT3>{
+ .*(\n?) ECHO;
+ <<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 && strcmp( file, "-" ) )
+ {
+ infilename = copy_string( file );
+ yyin = fopen( infilename, "r" );
+
+ if ( yyin == NULL )
+ lerrsf( _( "can't open %s" ), file );
+ }
+
+ else
+ {
+ yyin = stdin;
+ infilename = copy_string( "<stdin>" );
+ }
+
+ linenum = 1;
+ }
+
+
+/* Wrapper routines for accessing the scanner's malloc routines. */
+
+void *flex_alloc( size )
+size_t size;
+ {
+ return (void *) malloc( size );
+ }
+
+void *flex_realloc( ptr, size )
+void *ptr;
+size_t size;
+ {
+ return (void *) realloc( ptr, size );
+ }
+
+void flex_free( ptr )
+void *ptr;
+ {
+ if ( ptr )
+ free( ptr );
+ }
diff --git a/usr.bin/lex/skel.c b/usr.bin/lex/skel.c
new file mode 100644
index 0000000..962c830
--- /dev/null
+++ b/usr.bin/lex/skel.c
@@ -0,0 +1,1548 @@
+/* File created from flex.skl via mkskel.sh */
+
+#include "flexdef.h"
+
+const char *skel[] = {
+ "/* A lexical scanner generated by flex */",
+ "",
+ "/* Scanner skeleton version:",
+ " * $Header: /home/ncvs/src/usr.bin/lex/skel.c,v 1.1.1.2 1996/06/19 20:26:34 nate Exp $",
+ " */",
+ "",
+ "#define FLEX_SCANNER",
+ "#define YY_FLEX_MAJOR_VERSION 2",
+ "#define YY_FLEX_MINOR_VERSION 5",
+ "",
+ "%-",
+ "#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 */",
+ "",
+ "#if __STDC__",
+ "",
+ "#define YY_USE_PROTOS",
+ "#define YY_USE_CONST",
+ "",
+ "#endif /* __STDC__ */",
+ "#endif /* ! __cplusplus */",
+ "",
+ "#ifdef __TURBOC__",
+ " #pragma warn -rch",
+ " #pragma warn -use",
+ "#include <io.h>",
+ "#include <stdlib.h>",
+ "#define YY_USE_CONST",
+ "#define YY_USE_PROTOS",
+ "#endif",
+ "",
+ "#ifdef YY_USE_CONST",
+ "#define yyconst const",
+ "#else",
+ "#define yyconst",
+ "#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. The YYSTATE alias is for lex",
+ " * compatibility.",
+ " */",
+ "#define YY_START ((yy_start - 1) / 2)",
+ "#define YYSTATE YY_START",
+ "",
+ "/* 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\". */",
+ "#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;",
+ "%*",
+ "",
+ "#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_RESTORE_YY_MORE_OFFSET \\",
+ " 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 )",
+ "",
+ "/* The following is because we cannot portably get our hands on size_t",
+ " * (without autoconf's help, which isn't available because we want",
+ " * flex-generated scanners to compile on their own).",
+ " */",
+ "typedef unsigned int yy_size_t;",
+ "",
+ "",
+ "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.",
+ " */",
+ " yy_size_t yy_buf_size;",
+ "",
+ " /* Number of characters read into yy_ch_buf, not including EOB",
+ " * characters.",
+ " */",
+ " int yy_n_chars;",
+ "",
+ " /* Whether we \"own\" the buffer - i.e., we know we created it,",
+ " * and can realloc() it to grow it, and should free() it to",
+ " * delete it.",
+ " */",
+ " int yy_is_our_buffer;",
+ "",
+ " /* 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 we're considered to be at the beginning of a line.",
+ " * If so, '^' rules will be active on the next match, otherwise",
+ " * not.",
+ " */",
+ " int yy_at_bol;",
+ "",
+ " /* 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;",
+ "",
+ "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 ));",
+ "void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));",
+ "#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )",
+ "",
+ "YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));",
+ "YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str ));",
+ "YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));",
+ "%*",
+ "",
+ "static void *yy_flex_alloc YY_PROTO(( yy_size_t ));",
+ "static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));",
+ "static void yy_flex_free YY_PROTO(( void * ));",
+ "",
+ "#define yy_new_buffer yy_create_buffer",
+ "",
+ "#define yy_set_interactive(is_interactive) \\",
+ " { \\",
+ " if ( ! yy_current_buffer ) \\",
+ " yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \\",
+ " yy_current_buffer->yy_is_interactive = is_interactive; \\",
+ " }",
+ "",
+ "#define yy_set_bol(at_bol) \\",
+ " { \\",
+ " if ( ! yy_current_buffer ) \\",
+ " yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \\",
+ " yy_current_buffer->yy_at_bol = at_bol; \\",
+ " }",
+ "",
+ "#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)",
+ "",
+ "%% yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here",
+ "",
+ "%- 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(( yyconst 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.",
+ " */",
+ "",
+ "#ifndef YY_SKIP_YYWRAP",
+ "#ifdef __cplusplus",
+ "extern \"C\" int yywrap YY_PROTO(( void ));",
+ "#else",
+ "extern int yywrap YY_PROTO(( void ));",
+ "#endif",
+ "#endif",
+ "",
+ "%-",
+ "#ifndef YY_NO_UNPUT",
+ "static void yyunput YY_PROTO(( int c, char *buf_ptr ));",
+ "#endif",
+ "%*",
+ "",
+ "#ifndef yytext_ptr",
+ "static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));",
+ "#endif",
+ "",
+ "#ifdef YY_NEED_STRLEN",
+ "static int yy_flex_strlen YY_PROTO(( yyconst char * ));",
+ "#endif",
+ "",
+ "#ifndef YY_NO_INPUT",
+ "%- Standard (non-C++) definition",
+ "#ifdef __cplusplus",
+ "static int yyinput YY_PROTO(( void ));",
+ "#else",
+ "static int input YY_PROTO(( void ));",
+ "#endif",
+ "%*",
+ "#endif",
+ "",
+ "#if YY_STACK_USED",
+ "static int yy_start_stack_ptr = 0;",
+ "static int yy_start_stack_depth = 0;",
+ "static int *yy_start_stack = 0;",
+ "#ifndef YY_NO_PUSH_STATE",
+ "static void yy_push_state YY_PROTO(( int new_state ));",
+ "#endif",
+ "#ifndef YY_NO_POP_STATE",
+ "static void yy_pop_state YY_PROTO(( void ));",
+ "#endif",
+ "#ifndef YY_NO_TOP_STATE",
+ "static int yy_top_state YY_PROTO(( void ));",
+ "#endif",
+ "",
+ "#else",
+ "#define YY_NO_PUSH_STATE 1",
+ "#define YY_NO_POP_STATE 1",
+ "#define YY_NO_TOP_STATE 1",
+ "#endif",
+ "",
+ "#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_RULE_SETUP definition goes here",
+ "",
+ "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 )",
+ " {",
+ " yy_init = 0;",
+ "",
+ "#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_current_buffer =",
+ " yy_create_buffer( yyin, YY_BUF_SIZE );",
+ "",
+ " yy_load_buffer_state();",
+ " }",
+ "",
+ " 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",
+ "",
+ "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 = (int) (yy_cp - yytext_ptr) - 1;",
+ "",
+ " /* Undo the effects of YY_DO_BEFORE_ACTION. */",
+ " *yy_cp = yy_hold_char;",
+ " YY_RESTORE_YY_MORE_OFFSET",
+ "",
+ " 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 */",
+ "",
+ "%+",
+ "yyFlexLexer::yyFlexLexer( istream* arg_yyin, ostream* arg_yyout )",
+ " {",
+ " yyin = arg_yyin;",
+ " yyout = arg_yyout;",
+ " yy_c_buf_p = 0;",
+ " yy_init = 1;",
+ " yy_start = 0;",
+ " yy_flex_debug = 0;",
+ " yylineno = 1; // this will only get updated if %option yylineno",
+ "",
+ " yy_did_buffer_switch_on_eof = 0;",
+ "",
+ " yy_looking_for_trail_begin = 0;",
+ " yy_more_flag = 0;",
+ " yy_more_len = 0;",
+ " yy_more_offset = yy_prev_more_offset = 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",
+ " }",
+ "",
+ "yyFlexLexer::~yyFlexLexer()",
+ " {",
+ " delete yy_state_buf;",
+ " yy_delete_buffer( yy_current_buffer );",
+ " }",
+ "",
+ "void yyFlexLexer::switch_streams( istream* new_in, ostream* new_out )",
+ " {",
+ " if ( new_in )",
+ " {",
+ " yy_delete_buffer( yy_current_buffer );",
+ " yy_switch_to_buffer( yy_create_buffer( new_in, YY_BUF_SIZE ) );",
+ " }",
+ "",
+ " if ( new_out )",
+ " yyout = new_out;",
+ " }",
+ "",
+ "#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;",
+ " 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 single character, 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 = (int) (yy_c_buf_p - yytext_ptr) - 1;",
+ "",
+ " 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_current_buffer->yy_n_chars = 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 =",
+ " (int) (yy_c_buf_p - b->yy_ch_buf);",
+ "",
+ " if ( b->yy_is_our_buffer )",
+ " {",
+ " int new_size = b->yy_buf_size * 2;",
+ "",
+ " if ( new_size <= 0 )",
+ " b->yy_buf_size += b->yy_buf_size / 8;",
+ " else",
+ " b->yy_buf_size *= 2;",
+ "",
+ " b->yy_ch_buf = (char *)",
+ " /* Include room in for 2 EOB chars. */",
+ " yy_flex_realloc( (void *) b->yy_ch_buf,",
+ " b->yy_buf_size + 2 );",
+ " }",
+ " else",
+ " /* Can't grow it, we don't own it. */",
+ " b->yy_ch_buf = 0;",
+ "",
+ " 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 );",
+ "",
+ " yy_current_buffer->yy_n_chars = yy_n_chars;",
+ " }",
+ "",
+ " if ( yy_n_chars == 0 )",
+ " {",
+ " if ( number_to_move == YY_MORE_ADJ )",
+ " {",
+ " 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_ptr = &yy_current_buffer->yy_ch_buf[0];",
+ "",
+ " 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;",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifndef YY_NO_UNPUT",
+ "#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 += (int) (dest - source);",
+ " yy_bp += (int) (dest - source);",
+ " yy_current_buffer->yy_n_chars =",
+ " 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\" );",
+ " }",
+ "",
+ " *--yy_cp = (char) c;",
+ "",
+ "%% update yylineno here",
+ "",
+ " yytext_ptr = yy_bp;",
+ " yy_hold_char = *yy_cp;",
+ " yy_c_buf_p = yy_cp;",
+ " }",
+ "%-",
+ "#endif /* ifndef YY_NO_UNPUT */",
+ "%*",
+ "",
+ "",
+ "%-",
+ "#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 */",
+ " int offset = yy_c_buf_p - yytext_ptr;",
+ " ++yy_c_buf_p;",
+ "",
+ " switch ( yy_get_next_buffer() )",
+ " {",
+ " case EOB_ACT_LAST_MATCH:",
+ " /* This happens because yy_g_n_b()",
+ " * sees that we've accumulated a",
+ " * token and flags that we need to",
+ " * try matching the token before",
+ " * proceeding. But for input(),",
+ " * there's no matching to consider.",
+ " * So convert the EOB_ACT_LAST_MATCH",
+ " * to EOB_ACT_END_OF_FILE.",
+ " */",
+ "",
+ " /* Reset buffer status. */",
+ " yyrestart( yyin );",
+ "",
+ " /* fall through */",
+ "",
+ " case EOB_ACT_END_OF_FILE:",
+ " {",
+ " if ( yywrap() )",
+ " return EOF;",
+ "",
+ " if ( ! yy_did_buffer_switch_on_eof )",
+ " YY_NEW_FILE;",
+ "#ifdef __cplusplus",
+ " return yyinput();",
+ "#else",
+ " return input();",
+ "#endif",
+ " }",
+ "",
+ " case EOB_ACT_CONTINUE_SCAN:",
+ " yy_c_buf_p = yytext_ptr + offset;",
+ " break;",
+ " }",
+ " }",
+ " }",
+ "",
+ " 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;",
+ "",
+ "%% update BOL and yylineno",
+ "",
+ " 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()\" );",
+ "",
+ " b->yy_is_our_buffer = 1;",
+ "",
+ " 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 )",
+ " return;",
+ "",
+ " if ( b == yy_current_buffer )",
+ " yy_current_buffer = (YY_BUFFER_STATE) 0;",
+ "",
+ " if ( b->yy_is_our_buffer )",
+ " yy_flex_free( (void *) b->yy_ch_buf );",
+ "",
+ " yy_flex_free( (void *) b );",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifndef YY_ALWAYS_INTERACTIVE",
+ "#ifndef YY_NEVER_INTERACTIVE",
+ "extern int isatty YY_PROTO(( int ));",
+ "#endif",
+ "#endif",
+ "",
+ "#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",
+ "",
+ "%+",
+ "extern \"C\" int isatty YY_PROTO(( int ));",
+ "void yyFlexLexer::yy_init_buffer( YY_BUFFER_STATE b, istream* file )",
+ "%*",
+ "",
+ " {",
+ " yy_flush_buffer( b );",
+ "",
+ " b->yy_input_file = file;",
+ " b->yy_fill_buffer = 1;",
+ "",
+ "%-",
+ "#if YY_ALWAYS_INTERACTIVE",
+ " b->yy_is_interactive = 1;",
+ "#else",
+ "#if YY_NEVER_INTERACTIVE",
+ " b->yy_is_interactive = 0;",
+ "#else",
+ " b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;",
+ "#endif",
+ "#endif",
+ "%+",
+ " b->yy_is_interactive = 0;",
+ "%*",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "void yy_flush_buffer( YY_BUFFER_STATE b )",
+ "#else",
+ "void yy_flush_buffer( b )",
+ "YY_BUFFER_STATE b;",
+ "#endif",
+ "",
+ "%+",
+ "void yyFlexLexer::yy_flush_buffer( YY_BUFFER_STATE b )",
+ "%*",
+ " {",
+ " if ( ! b )",
+ " return;",
+ "",
+ " b->yy_n_chars = 0;",
+ "",
+ " /* 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[0] = YY_END_OF_BUFFER_CHAR;",
+ " b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;",
+ "",
+ " b->yy_buf_pos = &b->yy_ch_buf[0];",
+ "",
+ " b->yy_at_bol = 1;",
+ " b->yy_buffer_status = YY_BUFFER_NEW;",
+ "",
+ " if ( b == yy_current_buffer )",
+ " yy_load_buffer_state();",
+ " }",
+ "%*",
+ "",
+ "",
+ "#ifndef YY_NO_SCAN_BUFFER",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )",
+ "#else",
+ "YY_BUFFER_STATE yy_scan_buffer( base, size )",
+ "char *base;",
+ "yy_size_t size;",
+ "#endif",
+ " {",
+ " YY_BUFFER_STATE b;",
+ "",
+ " if ( size < 2 ||",
+ " base[size-2] != YY_END_OF_BUFFER_CHAR ||",
+ " base[size-1] != YY_END_OF_BUFFER_CHAR )",
+ " /* They forgot to leave room for the EOB's. */",
+ " return 0;",
+ "",
+ " b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );",
+ " if ( ! b )",
+ " YY_FATAL_ERROR( \"out of dynamic memory in yy_scan_buffer()\" );",
+ "",
+ " b->yy_buf_size = size - 2; /* \"- 2\" to take care of EOB's */",
+ " b->yy_buf_pos = b->yy_ch_buf = base;",
+ " b->yy_is_our_buffer = 0;",
+ " b->yy_input_file = 0;",
+ " b->yy_n_chars = b->yy_buf_size;",
+ " b->yy_is_interactive = 0;",
+ " b->yy_at_bol = 1;",
+ " b->yy_fill_buffer = 0;",
+ " b->yy_buffer_status = YY_BUFFER_NEW;",
+ "",
+ " yy_switch_to_buffer( b );",
+ "",
+ " return b;",
+ " }",
+ "%*",
+ "#endif",
+ "",
+ "",
+ "#ifndef YY_NO_SCAN_STRING",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str )",
+ "#else",
+ "YY_BUFFER_STATE yy_scan_string( yy_str )",
+ "yyconst char *yy_str;",
+ "#endif",
+ " {",
+ " int len;",
+ " for ( len = 0; yy_str[len]; ++len )",
+ " ;",
+ "",
+ " return yy_scan_bytes( yy_str, len );",
+ " }",
+ "%*",
+ "#endif",
+ "",
+ "",
+ "#ifndef YY_NO_SCAN_BYTES",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )",
+ "#else",
+ "YY_BUFFER_STATE yy_scan_bytes( bytes, len )",
+ "yyconst char *bytes;",
+ "int len;",
+ "#endif",
+ " {",
+ " YY_BUFFER_STATE b;",
+ " char *buf;",
+ " yy_size_t n;",
+ " int i;",
+ "",
+ " /* Get memory for full buffer, including space for trailing EOB's. */",
+ " n = len + 2;",
+ " buf = (char *) yy_flex_alloc( n );",
+ " if ( ! buf )",
+ " YY_FATAL_ERROR( \"out of dynamic memory in yy_scan_bytes()\" );",
+ "",
+ " for ( i = 0; i < len; ++i )",
+ " buf[i] = bytes[i];",
+ "",
+ " buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;",
+ "",
+ " b = yy_scan_buffer( buf, n );",
+ " if ( ! b )",
+ " YY_FATAL_ERROR( \"bad buffer in yy_scan_bytes()\" );",
+ "",
+ " /* It's okay to grow etc. this buffer, and we should throw it",
+ " * away when we're done.",
+ " */",
+ " b->yy_is_our_buffer = 1;",
+ "",
+ " return b;",
+ " }",
+ "%*",
+ "#endif",
+ "",
+ "",
+ "#ifndef YY_NO_PUSH_STATE",
+ "%-",
+ "#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 )",
+ " {",
+ " yy_size_t 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);",
+ " }",
+ "#endif",
+ "",
+ "",
+ "#ifndef YY_NO_POP_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]);",
+ " }",
+ "#endif",
+ "",
+ "",
+ "#ifndef YY_NO_TOP_STATE",
+ "%-",
+ "static int yy_top_state()",
+ "%+",
+ "int yyFlexLexer::yy_top_state()",
+ "%*",
+ " {",
+ " return yy_start_stack[yy_start_stack_ptr - 1];",
+ " }",
+ "#endif",
+ "",
+ "#ifndef YY_EXIT_FAILURE",
+ "#define YY_EXIT_FAILURE 2",
+ "#endif",
+ "",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "static void yy_fatal_error( yyconst char msg[] )",
+ "#else",
+ "static void yy_fatal_error( msg )",
+ "char msg[];",
+ "#endif",
+ " {",
+ " (void) fprintf( stderr, \"%s\\n\", msg );",
+ " exit( YY_EXIT_FAILURE );",
+ " }",
+ "",
+ "%+",
+ "",
+ "void yyFlexLexer::LexerError( yyconst char msg[] )",
+ " {",
+ " cerr << msg << '\\n';",
+ " exit( YY_EXIT_FAILURE );",
+ " }",
+ "%*",
+ "",
+ "",
+ "/* 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_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, yyconst char *s2, int n )",
+ "#else",
+ "static void yy_flex_strncpy( s1, s2, n )",
+ "char *s1;",
+ "yyconst char *s2;",
+ "int n;",
+ "#endif",
+ " {",
+ " register int i;",
+ " for ( i = 0; i < n; ++i )",
+ " s1[i] = s2[i];",
+ " }",
+ "#endif",
+ "",
+ "#ifdef YY_NEED_STRLEN",
+ "#ifdef YY_USE_PROTOS",
+ "static int yy_flex_strlen( yyconst char *s )",
+ "#else",
+ "static int yy_flex_strlen( s )",
+ "yyconst char *s;",
+ "#endif",
+ " {",
+ " register int n;",
+ " for ( n = 0; s[n]; ++n )",
+ " ;",
+ "",
+ " return n;",
+ " }",
+ "#endif",
+ "",
+ "",
+ "#ifdef YY_USE_PROTOS",
+ "static void *yy_flex_alloc( yy_size_t size )",
+ "#else",
+ "static void *yy_flex_alloc( size )",
+ "yy_size_t size;",
+ "#endif",
+ " {",
+ " return (void *) malloc( size );",
+ " }",
+ "",
+ "#ifdef YY_USE_PROTOS",
+ "static void *yy_flex_realloc( void *ptr, yy_size_t size )",
+ "#else",
+ "static void *yy_flex_realloc( ptr, size )",
+ "void *ptr;",
+ "yy_size_t size;",
+ "#endif",
+ " {",
+ " /* The cast to (char *) in the following accommodates both",
+ " * implementations that use char* generic pointers, and those",
+ " * that use void* generic pointers. It works with the latter",
+ " * because both ANSI C and C++ allow castless assignment from",
+ " * any pointer type to void*, and deal with argument conversions",
+ " * as though doing an assignment.",
+ " */",
+ " return (void *) realloc( (char *) 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 );",
+ " }",
+ "",
+ "#if YY_MAIN",
+ "int main()",
+ " {",
+ " yylex();",
+ " return 0;",
+ " }",
+ "#endif",
+ 0
+};
diff --git a/usr.bin/lex/sym.c b/usr.bin/lex/sym.c
new file mode 100644
index 0000000..7e29aca
--- /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.2 1996/06/19 20:26:39 nate 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]) != 0 )
+ {
+ 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 );
+ }
+
+
+/* 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. */
+ action_define( 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..738b76d
--- /dev/null
+++ b/usr.bin/lex/tblcmp.c
@@ -0,0 +1,887 @@
+/* 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.2 1996/06/19 20:26:43 nate 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),
+ (size_t) (MAX_XPAIRS_INCREMENT * sizeof( int )) );
+ }
+
+
+/* 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, (size_t) (current_max_xpairs * sizeof( int )) );
+
+ 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..623ca12
--- /dev/null
+++ b/usr.bin/lex/version.h
@@ -0,0 +1 @@
+#define FLEX_VERSION "2.5.4"
diff --git a/usr.bin/lex/yylex.c b/usr.bin/lex/yylex.c
new file mode 100644
index 0000000..918d468
--- /dev/null
+++ b/usr.bin/lex/yylex.c
@@ -0,0 +1,216 @@
+/* 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.2 1996/06/19 20:26:46 nate 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;
+ extern char *yytext;
+
+ 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 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 OPTION_OP:
+ fprintf( stderr, "%s ", yytext );
+ break;
+
+ case OPT_OUTFILE:
+ case OPT_PREFIX:
+ case CCE_ALNUM:
+ case CCE_ALPHA:
+ case CCE_BLANK:
+ case CCE_CNTRL:
+ case CCE_DIGIT:
+ case CCE_GRAPH:
+ case CCE_LOWER:
+ case CCE_PRINT:
+ case CCE_PUNCT:
+ case CCE_SPACE:
+ case CCE_UPPER:
+ case CCE_XDIGIT:
+ fprintf( stderr, "%s", yytext );
+ break;
+
+ case 0:
+ fprintf( stderr, _( "End Marker\n" ) );
+ break;
+
+ default:
+ fprintf( stderr,
+ _( "*Something Weird* - tok: %d val: %d\n" ),
+ toktype, yylval );
+ break;
+ }
+ }
+
+ return toktype;
+ }
diff --git a/usr.bin/limits/Makefile b/usr.bin/limits/Makefile
new file mode 100644
index 0000000..885930f
--- /dev/null
+++ b/usr.bin/limits/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+
+PROG= limits
+SRCS= limits.c
+
+CFLAGS+=-Wall
+LDADD+= -lutil
+DPADD+= ${LIBUTIL}
+
+BINOWN= root
+BINMODE=0555
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/limits/limits.1 b/usr.bin/limits/limits.1
new file mode 100644
index 0000000..64d3cbd
--- /dev/null
+++ b/usr.bin/limits/limits.1
@@ -0,0 +1,304 @@
+.\" Copyright (c) 1996 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is 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. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $Id$
+.\"
+.Dd January 15, 1996
+.Dt LIMITS 1
+.Os FreeBSD
+.Sh NAME
+.Nm limits
+.Nd Set or display process resource limits
+.Sh SYNOPSIS
+.Nm limits
+.Op Fl C Ar class | Fl U Ar user
+.Op Fl SHB
+.Op Fl e
+.Op Fl cdflmnstu Op val
+.Nm limits
+.Op Fl C Ar class | Fl U Ar user
+.Op Fl SHB
+.Op Fl cdflmnstu Op val
+.Op Fl E
+.Op Ar name=value ...
+.Op Ar command
+.Sh DESCRIPTION
+.Nm Limits
+ether prints or sets kernel resource limits, and may optionally set
+environment variables like
+.Xr env 1
+and run a program with the selected resources.
+Three uses of the
+.Nm limits
+command are possible:
+.Pp
+.Bl -hang -width indent
+.It Nm limits Op Ar limitflags
+.Op Ar name=value
+.Ar command
+.Pp
+This usage sets limits according to
+.Ar limitflags ,
+optionally sets environment variables given as
+.Ar name=value
+pairs, and then runs the specified command.
+.It Nm limits Op Ar limitflags
+.Pp
+This usage determines values of resource settings according to
+.Ar limitflags ,
+does not attempt to set them and outputs these values to
+standard output.
+By default, this will output the current kernel resource settings
+active for the calling process.
+Using the
+.Fl C Ar class
+or
+.Fl U Ar user
+flags, you may also display the current resource settings modified
+by the the appropriate login class resource limit entries from
+the
+.Xr login.conf 5
+login capabilities database.
+.It Nm limits Fl e Op Ar limitflags
+.Pp
+This usage determines values of resource settings according to
+.Ar limitflags ,
+but does not set them itself.
+Like the previous usage it outputs these values to standard
+output, except that it will emit them in
+.Em eval
+format, suitable for the calling shell.
+The calling shell is determined by examining the entries in the
+.Pa /proc
+filesystem for the parent process.
+If the shell is known (ie. it is one of sh, csh, bash, tcsh, ksh,
+pdksh or rc),
+.Nm limits
+emits 'limit' or 'ulimit' commands in the format understood by
+that shell.
+If the name of the shell cannot be determined, then the 'ulimit'
+format used by
+.Pa /bin/sh
+is used.
+.Pp
+This is very useful for setting limits used by scripts, or prior
+launching of daemons and other background tasks with specific
+resource limit settings, and provides the benefit of allowing
+global configuration of maximum resource usage by maintaining a
+central database of settings in the login class database.
+.Pp
+Within a shell script,
+.Nm limits
+will normally be used with eval within backticks as follows:
+.Pp
+.Dl eval `limits -e -C daemon`
+.Pp
+which causes the output of
+.Nm limits
+to be evaluated and set by the current shell.
+.El
+.Pp
+The value of limitflags specified in the above contains one or more of the
+following options:
+.Pp
+.Bl -tag -width "-d [limit]"
+.It Fl C Ar class
+Use current resource values, modified by the resource entries applicable
+for the login class "class".
+.It Fl U Ar user
+Use current resource values, modified by the resource entries applicable
+to the login class which "user" belongs to.
+If the user does not belong to a class, then the resource capabilities
+for the "default" class are used, if it exists, or the "root" class if
+the user is a superuser account.
+.It Fl S
+Selects display or setting of "soft" (or current) resource limits.
+If specific limits settings follow this switch, only soft limits are
+affected unless overridden later with either the
+.Fl H
+or
+.Fl B
+flags.
+.It Fl H
+Selects display or setting of "hard" (or maximum) resource limits.
+If specific limits settings follow this switch, only hard limits are
+affected until overridden later with either the
+.Fl S
+or
+.Fl B
+flags.
+.It Fl B
+Selects display or setting of both "soft" (current) or "hard" (maximum)
+resource limits.
+If specific limits settings follow this switch, both soft and hard
+limits are affected until overridden later with either the
+.Fl S
+or
+.Fl H
+flags.
+.Fl e
+Selects "eval mode" formatting for output.
+This is valid only on display mode and cannot be used when running a
+command.
+The exact syntax used for output depeneds upon the type of shell from
+which
+.Nm limits
+is invoked.
+.It Fl c Op Ar limit
+Selects or sets (if 'limit' is specified) the
+.Em coredumsize
+resource limit.
+A value of 0 disables core dumps.
+.It Fl d Op Ar limit
+Selects or sets (if 'limit' is specified) the
+.Em datasize
+resource limit.
+.It Fl f Op Ar limit
+Selects or sets the
+.Em filesize
+resource limit.
+.It Fl l Op Ar limit
+Selects or sets the
+.Em memorylocked
+resource limit.
+.It Fl m Op Ar limit
+Selects or sets the
+.Em memoryuse
+size limit
+.It Fl n Op Ar limit
+Selects or sets the
+.Em openfiles
+resource limit.
+.It Fl s Op Ar limit
+Selects or sets the
+.Em stacksize
+resource limit.
+.It Fl t Op Ar limit
+Selects or sets the
+.Em cputime
+resource limit.
+.It Fl u Op Ar limit
+Selects or sets the
+.Em maxproc
+resource limit.
+.Pp
+Valid values for 'limit' in the above set of flags consist of either the
+string 'infinity' or 'inf' for an infinite (or kernel-defined maximum)
+limit, or a numeric value maybe followed by a suffix.
+Values which relate to size default to a value in bytes, or one of the
+following suffixes may be used as a multiplier:
+.Pp
+.Bl -tag -offset indent -width "xxxx" -compact
+.It b
+512 byte blocks.
+.It k
+kilobytes (1024 bytes).
+.It m
+megabytes (1024*1024 bytes).
+.It g
+gigabytes.
+.It t
+terrabytes.
+.El
+.Pp
+The
+.Em cputime
+resource defaults to a number of seconds, but a multiplier may be
+used, and as with size values, multiple values separated by a valid
+suffix are added together:
+.Bl -tag -offset indent -width "xxxx" -compact
+.It s
+seconds.
+.It m
+minutes.
+.It h
+hours.
+.It d
+days.
+.It w
+weeks.
+.It y
+365 day years.
+.El
+.Pp
+.It Fl E
+The option
+.Sq Fl E
+causes
+.Nm limits
+to completely ignore the environment it inherits.
+.It Fl a
+This option forces all resource settings to be displayed even if
+other specific resource settings have been specified.
+For example, if you wish to disable core dumps when starting up
+the usenet news system, but wish to set all other resource settings
+as well that apply to the 'news' account, you might use:
+.Pp
+.Dl eval `limits -U news -aBec 0`
+.Pp
+As with the
+.Xr setrlimit 3
+call, only the superuser may raise process "hard" resource limits.
+Non-root users may, however, lower them or change "soft" resource limits
+within to any value below the hard limit.
+When invoked to execute a program, the failure of
+.Nm limits
+to raise a hard limit is considered a fatal error.
+.El
+.Sh DIAGNOSTICS
+.Nm Limits
+exits with EXIT_FAILURE if usage is incorrect in any way; ie. an invalid
+option, or set/display options are selected in the same invocation,
+.Fl e
+is used when running a program, etc.
+When run in display or eval mode,
+.Nm limits
+exits with with a status of EXIT_SUCCESS.
+When run in command mode and execution of the command succeeds, the exit status
+will be whatever the executed program returns.
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr env 1 ,
+.Xr limit 1 ,
+.Xr sh 1 ,
+.Xr ulimit 1 ,
+.Xr getrlimit 3 ,
+.Xr setrlimit 3 ,
+.Xr login_cap 3 ,
+.Xr login.conf 5
+.Sh BUGS
+.Nm Limits
+does not handle commands with equal (``='') signs in their
+names, for obvious reasons.
+.Pp
+When eval output is selected, the /proc filesystem must be installed
+and mounted for the shell to be correctly determined, and therefore
+output syntax correct for the running shell.
+The default output is valid for /bin/sh, so this means that any
+usage of
+.Nm limits
+in eval mode prior mounting /proc may only occur in standard bourne
+shell scripts.
+.Pp
+.Nm Limits
+makes no effort to ensure that resource settings emitted or displayed
+are valid and settable by the current user.
+Only a superuser account may raise hard limits, and and when doing so
+the FreeBSD kernel will silently lower limits to values less than
+specified if the values given are too high.
diff --git a/usr.bin/limits/limits.c b/usr.bin/limits/limits.c
new file mode 100644
index 0000000..98a2b4c
--- /dev/null
+++ b/usr.bin/limits/limits.c
@@ -0,0 +1,630 @@
+/*-
+ * Copyright (c) 1997 by
+ * David L. Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is 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. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Display/change(+runprogram)/eval resource limits.
+ *
+ * $Id: limits.c,v 1.3 1997/03/29 04:30:26 imp Exp $
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <login_cap.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+enum
+{
+ SH_NONE,
+ SH_SH, /* sh */
+ SH_CSH, /* csh */
+ SH_BASH, /* gnu bash */
+ SH_TCSH, /* tcsh */
+ SH_KSH, /* (pd)ksh */
+ SH_ZSH, /* zsh */
+ SH_RC, /* rc or es */
+ SH_NUMBER
+};
+
+
+/* eval emitter for popular shells.
+ * Why aren't there any standards here? Most shells support either
+ * the csh 'limit' or sh 'ulimit' command, but each varies just
+ * enough that they aren't very compatible from one to the other.
+ */
+static struct {
+ const char * name; /* Name of shell */
+ const char * inf; /* Name used for 'unlimited' resource */
+ const char * cmd; /* Intro text */
+ const char * hard; /* Hard limit text */
+ const char * soft; /* Soft limit text */
+ const char * both; /* Hard+Soft limit text */
+ struct {
+ const char * pfx;
+ const char * sfx;
+ int divisor;
+ } lprm[RLIM_NLIMITS];
+} shellparm[] =
+{
+ { "", "infinity", "Resource limits%s%s:\n", "-max", "-cur", "",
+ {
+ { " cputime%-4s %8s", " secs\n", 1 },
+ { " filesize%-4s %8s", " kb\n", 1024 },
+ { " datasize%-4s %8s", " kb\n", 1024 },
+ { " stacksize%-4s %8s", " kb\n", 1024 },
+ { " coredumpsize%-4s %8s", " kb\n", 1024 },
+ { " memoryuse%-4s %8s", " kb\n", 1024 },
+ { " memorylocked%-4s %8s", " kb\n", 1024 },
+ { " maxprocesses%-4s %8s", "\n", 1 },
+ { " openfiles%-4s %8s", "\n", 1 }
+ }
+ },
+ { "sh", "unlimited", "", " -H", " -S", "",
+ {
+ { "ulimit%s -t %s", ";\n", 1 },
+ { "ulimit%s -f %s", ";\n", 512 },
+ { "ulimit%s -d %s", ";\n", 1024 },
+ { "ulimit%s -s %s", ";\n", 1024 },
+ { "ulimit%s -c %s", ";\n", 512 },
+ { "ulimit%s -m %s", ";\n", 1024 },
+ { "ulimit%s -l %s", ";\n", 1024 },
+ { "ulimit%s -u %s", ";\n", 1 },
+ { "ulimit%s -n %s", ";\n", 1 }
+ }
+ },
+ { "csh", "unlimited", "", " -h", "", NULL,
+ {
+ { "limit%s cputime %s", ";\n", 1 },
+ { "limit%s filesize %s", ";\n", 1024 },
+ { "limit%s datasize %s", ";\n", 1024 },
+ { "limit%s stacksize %s", ";\n", 1024 },
+ { "limit%s coredumpsize %s", ";\n", 1024 },
+ { "limit%s memoryuse %s", ";\n", 1024 },
+ { "limit%s memorylocked %s", ";\n", 1024 },
+ { "limit%s maxproc %s", ";\n", 1 },
+ { "limit%s openfiles %s", ";\n", 1 }
+ }
+ },
+ { "bash|bash2", "unlimited", "", " -H", " -S", "",
+ {
+ { "ulimit%s -t %s", ";\n", 1 },
+ { "ulimit%s -f %s", ";\n", 1024 },
+ { "ulimit%s -d %s", ";\n", 1024 },
+ { "ulimit%s -s %s", ";\n", 1024 },
+ { "ulimit%s -c %s", ";\n", 1024 },
+ { "ulimit%s -m %s", ";\n", 1024 },
+ { "ulimit%s -l %s", ";\n", 1024 },
+ { "ulimit%s -u %s", ";\n", 1 },
+ { "ulimit%s -n %s", ";\n", 1 }
+ }
+ },
+ { "tcsh", "unlimited", "", " -h", "", NULL,
+ {
+ { "limit%s cputime %s", ";\n", 1 },
+ { "limit%s filesize %s", ";\n", 1024 },
+ { "limit%s datasize %s", ";\n", 1024 },
+ { "limit%s stacksize %s", ";\n", 1024 },
+ { "limit%s coredumpsize %s", ";\n", 1024 },
+ { "limit%s memoryuse %s", ";\n", 1024 },
+ { "limit%s memorylocked %s", ";\n", 1024 },
+ { "limit%s maxproc %s", ";\n", 1 },
+ { "limit%s descriptors %s", ";\n", 1 }
+ }
+ },
+ { "ksh|pdksh", "unlimited", "", " -H", " -S", "",
+ {
+ { "ulimit%s -t %s", ";\n", 1 },
+ { "ulimit%s -f %s", ";\n", 512 },
+ { "ulimit%s -d %s", ";\n", 1024 },
+ { "ulimit%s -s %s", ";\n", 1024 },
+ { "ulimit%s -c %s", ";\n", 512 },
+ { "ulimit%s -m %s", ";\n", 1024 },
+ { "ulimit%s -l %s", ";\n", 1024 },
+ { "ulimit%s -p %s", ";\n", 1 },
+ { "ulimit%s -n %s", ";\n", 1 }
+ }
+ },
+ { "zsh", "unlimited", "", " -H", " -S", "",
+ {
+ { "ulimit%s -t %s", ";\n", 1 },
+ { "ulimit%s -f %s", ";\n", 512 },
+ { "ulimit%s -d %s", ";\n", 1024 },
+ { "ulimit%s -s %s", ";\n", 1024 },
+ { "ulimit%s -c %s", ";\n", 512 },
+ { "ulimit%s -m %s", ";\n", 1024 },
+ { "ulimit%s -l %s", ";\n", 1024 },
+ { "ulimit%s -u %s", ";\n", 1 },
+ { "ulimit%s -n %s", ";\n", 1 }
+ }
+ },
+ { "rc|es", "unlimited", "", " -h", "", NULL,
+ {
+ { "limit%s cputime %s", ";\n", 1 },
+ { "limit%s filesize %s", ";\n", 1024 },
+ { "limit%s datasize %s", ";\n", 1024 },
+ { "limit%s stacksize %s", ";\n", 1024 },
+ { "limit%s coredumpsize %s", ";\n", 1024 },
+ { "limit%s memoryuse %s", ";\n", 1024 },
+ { "limit%s lockedmemory %s", ";\n", 1024 },
+ { "limit%s processes %s", ";\n", 1 },
+ { "limit%s descriptors %s", ";\n", 1 }
+ }
+ },
+ { NULL }
+};
+
+static struct {
+ const char * cap;
+ rlim_t (*func)(login_cap_t *, const char *, rlim_t, rlim_t);
+} resources[RLIM_NLIMITS] = {
+ { "cputime", login_getcaptime },
+ { "filesize", login_getcapsize },
+ { "datasize", login_getcapsize },
+ { "stacksize", login_getcapsize },
+ { "coredumpsize", login_getcapsize },
+ { "memoryuse", login_getcapsize },
+ { "memorylocked", login_getcapsize },
+ { "maxproc", login_getcapnum, },
+ { "openfiles", login_getcapnum }
+};
+
+/*
+ * One letter for each resource levels.
+ * NOTE: There is a dependancy on the corresponding
+ * letter index being equal to the resource number.
+ * If sys/resource.h defines are changed, this needs
+ * to be modified accordingly!
+ */
+
+#define RCS_STRING "tfdscmlun"
+
+static rlim_t resource_num(int which, int ch, const char *str);
+static void usage(const char *msg, ...);
+static int getshelltype(void);
+static void print_limit(rlim_t limit, unsigned divisor, const char *inf,
+ const char *pfx, const char *sfx, const char *which);
+extern char **environ;
+
+static const char rcs_string[] = RCS_STRING;
+
+int
+main(int argc, char *argv[])
+{
+ char *p, *cls = NULL;
+ char *cleanenv[1];
+ struct passwd * pwd = NULL;
+ int rcswhich, shelltype;
+ int i, num_limits = 0;
+ int ch, doeval = 0, doall = 0;
+ login_cap_t * lc = NULL;
+ enum { ANY=0, SOFT=1, HARD=2, BOTH=3, DISPLAYONLY=4 } type = ANY;
+ enum { RCSUNKNOWN=0, RCSSET=1, RCSSEL=2 } todo = RCSUNKNOWN;
+ int which_limits[RLIM_NLIMITS];
+ rlim_t set_limits[RLIM_NLIMITS];
+ struct rlimit limits[RLIM_NLIMITS];
+
+ /* init resource tables */
+ for (i = 0; i < RLIM_NLIMITS; i++) {
+ which_limits[i] = 0; /* Don't set/display any */
+ set_limits[i] = RLIM_INFINITY;
+ /* Get current resource values */
+ getrlimit(i, &limits[i]);
+ }
+
+ optarg = NULL;
+ while ((ch = getopt(argc, argv, ":EeC:U:BSHac:d:f:l:m:n:s:t:u:")) != -1) {
+ switch(ch) {
+ case 'a':
+ doall = 1;
+ break;
+ case 'E':
+ environ = cleanenv;
+ cleanenv[0] = NULL;
+ break;
+ case 'e':
+ doeval = 1;
+ break;
+ case 'C':
+ cls = optarg;
+ break;
+ case 'U':
+ if ((pwd = getpwnam(optarg)) == NULL) {
+ if (!isdigit(*optarg) ||
+ (pwd = getpwuid(atoi(optarg))) == NULL) {
+ usage("Invalid user `%s'\n", optarg);
+ }
+ }
+ break;
+ case 'H':
+ type = HARD;
+ break;
+ case 'S':
+ type = SOFT;
+ break;
+ case 'B':
+ type = SOFT|HARD;
+ break;
+ default:
+ case ':': /* Without arg */
+ if ((p = strchr(rcs_string, optopt)) != NULL) {
+ int rcswhich = p - rcs_string;
+ if (optarg && *optarg == '-') { /* 'arg' is actually a switch */
+ --optind; /* back one arg, and make arg NULL */
+ optarg = NULL;
+ }
+ todo = optarg == NULL ? RCSSEL : RCSSET;
+ if (type == ANY)
+ type = BOTH;
+ which_limits[rcswhich] = optarg ? type : DISPLAYONLY;
+ set_limits[rcswhich] = resource_num(rcswhich, optopt, optarg);
+ num_limits++;
+ break;
+ }
+ /* FALLTHRU */
+ case '?':
+ usage(NULL);
+ }
+ optarg = NULL;
+ }
+
+ /* If user was specified, get class from that */
+ if (pwd != NULL)
+ lc = login_getpwclass(pwd);
+ else if (cls != NULL && *cls != '\0') {
+ lc = login_getclassbyname(cls, NULL);
+ if (lc == NULL || strcmp(cls, lc->lc_class) != 0)
+ fprintf(stderr, "login class '%s' non-existent, using %s\n",
+ cls, lc?lc->lc_class:"current settings");
+ }
+
+ /* If we have a login class, update resource table from that */
+ if (lc != NULL) {
+ for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) {
+ char str[40];
+ rlim_t val;
+
+ /* current value overridden by resourcename or resourcename-cur */
+ sprintf(str, "%s-cur", resources[rcswhich].cap);
+ val = resources[rcswhich].func(lc, resources[rcswhich].cap, limits[rcswhich].rlim_cur, limits[rcswhich].rlim_cur);
+ limits[rcswhich].rlim_cur = resources[rcswhich].func(lc, str, val, val);
+ /* maximum value overridden by resourcename or resourcename-max */
+ sprintf(str, "%s-max", resources[rcswhich].cap);
+ val = resources[rcswhich].func(lc, resources[rcswhich].cap, limits[rcswhich].rlim_max, limits[rcswhich].rlim_max);
+ limits[rcswhich].rlim_max = resources[rcswhich].func(lc, str, val, val);
+ }
+ }
+
+ /* now, let's determine what we wish to do with all this */
+
+ argv += optind;
+
+ /* If we're setting limits or doing an eval (ie. we're not just
+ * displaying), then check that hard limits are not lower than
+ * soft limits, and force rasing the hard limit if we need to if
+ * we are raising the soft limit, or lower the soft limit if we
+ * are lowering the hard limit.
+ */
+ if ((*argv || doeval) && getuid() == 0) {
+
+ for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) {
+ if (limits[rcswhich].rlim_max != RLIM_INFINITY) {
+ if (limits[rcswhich].rlim_cur == RLIM_INFINITY) {
+ limits[rcswhich].rlim_max = RLIM_INFINITY;
+ which_limits[rcswhich] |= HARD;
+ } else if (limits[rcswhich].rlim_cur > limits[rcswhich].rlim_max) {
+ if (which_limits[rcswhich] == SOFT) {
+ limits[rcswhich].rlim_max = limits[rcswhich].rlim_cur;
+ which_limits[rcswhich] |= HARD;
+ } else if (which_limits[rcswhich] == HARD) {
+ limits[rcswhich].rlim_cur = limits[rcswhich].rlim_max;
+ which_limits[rcswhich] |= SOFT;
+ } else {
+ /* else.. if we're specifically setting both to
+ * silly values, then let it error out.
+ */
+ }
+ }
+ }
+ }
+ }
+
+ /* See if we've overridden anything specific on the command line */
+ if (num_limits && todo == RCSSET) {
+ for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) {
+ if (which_limits[rcswhich] & HARD)
+ limits[rcswhich].rlim_max = set_limits[rcswhich];
+ if (which_limits[rcswhich] & SOFT)
+ limits[rcswhich].rlim_cur = set_limits[rcswhich];
+ }
+ }
+
+ /* If *argv is not NULL, then we are being asked to
+ * (perhaps) set environment variables and run a program
+ */
+ if (*argv) {
+ if (doeval)
+ usage("-e cannot be used with `cmd' option\n");
+
+ login_close(lc);
+
+ /* set leading environment variables, like eval(1) */
+ while (*argv && (p = strchr(*argv, '=')))
+ (void)setenv(*argv++, ++p, 1);
+
+ /* Set limits */
+ for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) {
+ if (doall || num_limits == 0 || which_limits[rcswhich] != 0)
+ if (setrlimit(rcswhich, &limits[rcswhich]) == -1)
+ err(1, "setrlimit %s", resources[rcswhich].cap);
+ }
+
+ if (*argv == NULL)
+ usage(NULL);
+
+ execvp(*argv, argv);
+ err(1, "%s", *argv);
+ }
+
+ shelltype = doeval ? getshelltype() : SH_NONE;
+
+ if (type == ANY) /* Default to soft limits */
+ type = SOFT;
+
+ /* Display limits */
+ printf(shellparm[shelltype].cmd,
+ lc ? " for class " : " (current)",
+ lc ? lc->lc_class : "");
+
+ for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) {
+ if (doall || num_limits == 0 || which_limits[rcswhich] != 0) {
+ if (which_limits[rcswhich] == ANY || which_limits[rcswhich])
+ which_limits[rcswhich] = type;
+ if (shellparm[shelltype].lprm[rcswhich].pfx) {
+ if (shellparm[shelltype].both && limits[rcswhich].rlim_cur == limits[rcswhich].rlim_max) {
+ print_limit(limits[rcswhich].rlim_max,
+ shellparm[shelltype].lprm[rcswhich].divisor,
+ shellparm[shelltype].inf,
+ shellparm[shelltype].lprm[rcswhich].pfx,
+ shellparm[shelltype].lprm[rcswhich].sfx,
+ shellparm[shelltype].both);
+ } else {
+ if (which_limits[rcswhich] & HARD) {
+ print_limit(limits[rcswhich].rlim_max,
+ shellparm[shelltype].lprm[rcswhich].divisor,
+ shellparm[shelltype].inf,
+ shellparm[shelltype].lprm[rcswhich].pfx,
+ shellparm[shelltype].lprm[rcswhich].sfx,
+ shellparm[shelltype].hard);
+ }
+ if (which_limits[rcswhich] & SOFT) {
+ print_limit(limits[rcswhich].rlim_cur,
+ shellparm[shelltype].lprm[rcswhich].divisor,
+ shellparm[shelltype].inf,
+ shellparm[shelltype].lprm[rcswhich].pfx,
+ shellparm[shelltype].lprm[rcswhich].sfx,
+ shellparm[shelltype].soft);
+ }
+ }
+ }
+ }
+ }
+
+ login_close(lc);
+ exit(EXIT_SUCCESS);
+}
+
+
+static void
+usage(char const *msg, ...)
+{
+ if (msg) {
+ va_list argp;
+ va_start(argp, msg);
+ vfprintf(stderr, msg, argp);
+ va_end(argp);
+ }
+ (void)fprintf(stderr,
+ "limits [-C class|-U user] [-eaSHBE] [-cdflmnstu [val]] [[name=val ...] cmd]\n");
+ exit(EXIT_FAILURE);
+}
+
+static void
+print_limit(rlim_t limit, unsigned divisor, const char * inf, const char * pfx, const char * sfx, const char * which)
+{
+ char numbr[64];
+
+ if (limit == RLIM_INFINITY)
+ strcpy(numbr, inf);
+ else
+ sprintf(numbr, "%qd", (quad_t)((limit + divisor/2) / divisor));
+ printf(pfx, which, numbr);
+ printf(sfx, which);
+
+}
+
+
+static rlim_t
+resource_num(int which, int ch, const char *str)
+{
+ rlim_t res = RLIM_INFINITY;
+
+ if (str != NULL &&
+ !(strcasecmp(str, "inf") == 0 ||
+ strcasecmp(str, "infinity") == 0 ||
+ strcasecmp(str, "unlimit") == 0 ||
+ strcasecmp(str, "unlimited") == 0)) {
+ const char * s = str;
+ char *e;
+
+ switch (which) {
+ case RLIMIT_CPU: /* time values */
+ errno = 0;
+ res = 0;
+ while (*s) {
+ rlim_t tim = strtoq(s, &e, 0);
+ if (e == NULL || e == s || errno)
+ break;
+ switch (*e++) {
+ case 0: /* end of string */
+ e--;
+ default:
+ case 's': case 'S': /* seconds */
+ break;
+ case 'm': case 'M': /* minutes */
+ tim *= 60L;
+ break;
+ case 'h': case 'H': /* hours */
+ tim *= (60L * 60L);
+ break;
+ case 'd': case 'D': /* days */
+ tim *= (60L * 60L * 24L);
+ break;
+ case 'w': case 'W': /* weeks */
+ tim *= (60L * 60L * 24L * 7L);
+ case 'y': case 'Y': /* Years */
+ tim *= (60L * 60L * 24L * 365L);
+ }
+ s = e;
+ res += tim;
+ }
+ break;
+ case RLIMIT_FSIZE: /* Size values */
+ case RLIMIT_DATA:
+ case RLIMIT_STACK:
+ case RLIMIT_CORE:
+ case RLIMIT_RSS:
+ case RLIMIT_MEMLOCK:
+ errno = 0;
+ res = 0;
+ while (*s) {
+ rlim_t mult, tim = strtoq(s, &e, 0);
+ if (e == NULL || e == s || errno)
+ break;
+ switch (*e++) {
+ case 0: /* end of string */
+ e--;
+ default:
+ mult = 1;
+ break;
+ case 'b': case 'B': /* 512-byte blocks */
+ mult = 512;
+ break;
+ case 'k': case 'K': /* 1024-byte Kilobytes */
+ mult = 1024;
+ break;
+ case 'm': case 'M': /* 1024-k kbytes */
+ mult = 1024 * 1024;
+ break;
+ case 'g': case 'G': /* 1Gbyte */
+ mult = 1024 * 1024 * 1024;
+ break;
+ case 't': case 'T': /* 1TBte */
+ mult = 1024LL * 1024LL * 1024LL * 1024LL;
+ break;
+ }
+ s = e;
+ res += (tim * mult);
+ }
+ break;
+ case RLIMIT_NPROC:
+ case RLIMIT_NOFILE:
+ res = strtoq(s, &e, 0);
+ s = e;
+ break;
+ }
+ if (*s)
+ usage("invalid value -%c `%s'\n", ch, str);
+ }
+ return res;
+}
+
+
+static int
+getshellbyname(const char * shell)
+{
+ int i;
+ const char * q;
+ const char * p = strrchr(shell, '/');
+
+ p = p ? ++p : shell;
+ for (i = 0; (q = shellparm[i].name) != NULL; i++) {
+ while (*q) {
+ int j = strcspn(q, "|");
+
+ if (j == 0)
+ break;
+ if (strncmp(p, q, j) == 0)
+ return i;
+ if (*(q += j))
+ ++q;
+ }
+ }
+ return SH_SH;
+}
+
+
+/*
+ * Determine the type of shell our parent process is
+ * This is quite tricky, not 100% reliable and probably
+ * not nearly as thorough as it should be. Basically, this
+ * is a "best guess" only, but hopefully will work in
+ * most cases.
+ */
+
+static int
+getshelltype(void)
+{
+ pid_t ppid = getppid();
+
+ if (ppid != 1) {
+ FILE * fp;
+ struct stat st;
+ char procdir[MAXPATHLEN], buf[128];
+ int l = sprintf(procdir, "/proc/%ld/", (long)ppid);
+ char * shell = getenv("SHELL");
+
+ if (shell != NULL && stat(shell, &st) != -1) {
+ struct stat st1;
+
+ strcpy(procdir+l, "file");
+ /* $SHELL is actual shell? */
+ if (stat(procdir, &st1) != -1 && memcmp(&st, &st1, sizeof st) == 0)
+ return getshellbyname(shell);
+ }
+ strcpy(procdir+l, "status");
+ if (stat(procdir, &st) == 0 && (fp = fopen(procdir, "r")) != NULL) {
+ char * p = fgets(buf, sizeof buf, fp)==NULL ? NULL : strtok(buf, " \t");
+ fclose(fp);
+ if (p != NULL)
+ return getshellbyname(p);
+ }
+ }
+ return SH_SH;
+}
+
diff --git a/usr.bin/locate/Makefile b/usr.bin/locate/Makefile
new file mode 100644
index 0000000..45f82b9
--- /dev/null
+++ b/usr.bin/locate/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id$
+
+SUBDIR= bigram code locate
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/locate/Makefile.inc b/usr.bin/locate/Makefile.inc
new file mode 100644
index 0000000..0f80876
--- /dev/null
+++ b/usr.bin/locate/Makefile.inc
@@ -0,0 +1,3 @@
+# $Id$
+
+LIBEXECDIR?= /usr/libexec
diff --git a/usr.bin/locate/bigram/Makefile b/usr.bin/locate/bigram/Makefile
new file mode 100644
index 0000000..fbba14d
--- /dev/null
+++ b/usr.bin/locate/bigram/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= locate.bigram
+NOMAN= noman
+BINDIR= ${LIBEXECDIR}
+CFLAGS+= -I${.CURDIR}/../locate
+
+.include "../Makefile.inc"
+.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..663428a
--- /dev/null
+++ b/usr.bin/locate/bigram/locate.bigram.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 < sorted_file_names | sort -nr |
+ * awk 'NR <= 128 { printf $2 }' > bigrams
+ *
+ * List bigrams for 'updatedb' script.
+ * Use 'code' to encode a file using this output.
+ */
+
+#include <stdio.h>
+#include <sys/param.h> /* for MAXPATHLEN */
+#include "locate.h"
+
+u_char buf1[MAXPATHLEN] = " ";
+u_char buf2[MAXPATHLEN];
+u_int bigram[UCHAR_MAX + 1][UCHAR_MAX + 1];
+
+int
+main(void)
+{
+ register u_char *cp;
+ register u_char *oldpath = buf1, *path = buf2;
+ register u_int i, j;
+
+ while (fgets(path, sizeof(buf2), stdin) != NULL) {
+
+ /*
+ * We don't need remove newline character '\n'.
+ * '\n' is less than ASCII_MIN and will be later
+ * ignored at output.
+ */
+
+
+ /* skip longest common prefix */
+ for (cp = path; *cp == *oldpath; cp++, oldpath++)
+ if (*cp == '\0')
+ break;
+
+ while (*cp != '\0' && *(cp + 1) != '\0') {
+ bigram[(u_char)*cp][(u_char)*(cp + 1)]++;
+ cp += 2;
+ }
+
+ /* swap pointers */
+ if (path == buf1) {
+ path = buf2;
+ oldpath = buf1;
+ } else {
+ path = buf1;
+ oldpath = buf2;
+ }
+ }
+
+ /* output, boundary check */
+ for (i = ASCII_MIN; i <= ASCII_MAX; i++)
+ for (j = ASCII_MIN; j <= ASCII_MAX; j++)
+ if (bigram[i][j] != 0)
+ (void)printf("%4u %c%c\n", bigram[i][j], i, j);
+
+ exit(0);
+}
diff --git a/usr.bin/locate/code/Makefile b/usr.bin/locate/code/Makefile
new file mode 100644
index 0000000..a7d8e80
--- /dev/null
+++ b/usr.bin/locate/code/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= locate.code
+CFLAGS+=-I${.CURDIR}/../locate
+NOMAN= noman
+BINDIR= ${LIBEXECDIR}
+
+.include "../Makefile.inc"
+.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..47d2059
--- /dev/null
+++ b/usr.bin/locate/code/locate.code.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+ * 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.
+ *
+ * $Id: locate.code.c,v 1.9 1997/02/22 19:55:43 peter Exp $
+ */
+
+#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
+ * 31 an 8 bit char followed
+ * 128-255 bigram codes (128 most common, as determined by 'updatedb')
+ * 32-127 single character (printable) ascii residue (ie, literal)
+ *
+ * The locate database store any character except newline ('\n')
+ * and NUL ('\0'). The 8-bit character support don't wast extra
+ * space until you have characters in file names less than 32
+ * or greather than 127.
+ *
+ *
+ * SEE ALSO: updatedb.sh, ../bigram/locate.bigram.c
+ *
+ * AUTHOR: James A. Woods, Informatics General Corp.,
+ * NASA Ames Research Center, 10/82
+ * 8-bit file names characters:
+ * Wolfram Schneider, Berlin September 1996
+ */
+
+#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 */
+
+u_char buf1[MAXPATHLEN] = " ";
+u_char buf2[MAXPATHLEN];
+u_char bigrams[BGBUFSIZE + 1] = { 0 };
+
+#define LOOKUP 1 /* use a lookup array instead a function, 3x faster */
+
+#ifdef LOOKUP
+#define BGINDEX(x) (big[(u_char)*x][(u_char)*(x + 1)])
+typedef short bg_t;
+bg_t big[UCHAR_MAX + 1][UCHAR_MAX + 1];
+#else
+#define BGINDEX(x) bgindex(x)
+typedef int bg_t;
+int bgindex __P((char *));
+#endif /* LOOKUP */
+
+
+void usage __P((void));
+extern int optind;
+extern int optopt;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register u_char *cp, *oldpath, *path;
+ int ch, code, count, diffcount, oldcount;
+ FILE *fp;
+ register int i, j;
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch(ch) {
+ 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);
+
+#ifdef LOOKUP
+ /* init lookup table */
+ for (i = 0; i < UCHAR_MAX + 1; i++)
+ for (j = 0; j < UCHAR_MAX + 1; j++)
+ big[i][j] = (bg_t)-1;
+
+ for (cp = bigrams, i = 0; *cp != '\0'; i += 2, cp += 2)
+ big[(u_char)*cp][(u_char)*(cp + 1)] = (bg_t)i;
+
+#endif /* LOOKUP */
+
+ oldpath = buf1;
+ path = buf2;
+ oldcount = 0;
+
+ while (fgets(path, sizeof(buf2), stdin) != NULL) {
+
+ /* skip empty lines */
+ if (*path == '\n')
+ continue;
+
+ /* remove newline */
+ for (cp = path; *cp != '\0'; cp++) {
+#ifndef LOCATE_CHAR30
+ /* old locate implementations core'd for char 30 */
+ if (*cp == SWITCH)
+ *cp = '?';
+ else
+#endif /* !LOCATE_CHAR30 */
+
+ /* chop newline */
+ if (*cp == '\n')
+ *cp = '\0';
+ }
+
+ /* Skip longest common prefix. */
+ for (cp = path; *cp == *oldpath; cp++, oldpath++)
+ if (*cp == '\0')
+ 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 != '\0') {
+ /* print *two* characters */
+
+ if ((code = BGINDEX(cp)) != (bg_t)-1) {
+ /*
+ * print *one* as bigram
+ * Found, so mark byte with
+ * parity bit.
+ */
+ if (putchar((code / 2) | PARITY) == EOF)
+ err(1, "stdout");
+ cp += 2;
+ }
+
+ else {
+ for (i = 0; i < 2; i++) {
+ if (*cp == '\0')
+ break;
+
+ /* print umlauts in file names */
+ if (*cp < ASCII_MIN ||
+ *cp > ASCII_MAX) {
+ if (putchar(UMLAUT) == EOF ||
+ putchar(*cp++) == EOF)
+ err(1, "stdout");
+ }
+
+ else {
+ /* normal character */
+ if(putchar(*cp++) == EOF)
+ err(1, "stdout");
+ }
+ }
+
+ }
+ }
+
+ 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);
+}
+
+#ifndef LOOKUP
+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));
+}
+#endif /* !LOOKUP */
+
+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..5e20b83
--- /dev/null
+++ b/usr.bin/locate/locate/Makefile
@@ -0,0 +1,25 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id$
+
+PROG= locate
+SRCS= util.c locate.c
+CFLAGS+= -I${.CURDIR} -DMMAP # -DDEBUG (print time) -O2 (10% faster)
+MAN1= locate.1
+MAN8= locate.updatedb.8
+SCRIPTS= updatedb mklocatedb concatdb
+MLINKS+= locate.updatedb.8 updatedb.8
+
+
+beforeinstall:
+.for script in ${SCRIPTS}
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/${script}.sh ${DESTDIR}${LIBEXECDIR}/locate.${script}
+.endfor
+
+# only /usr/src/etc/Makefile install files in /etc
+# ${INSTALL} -c -o root -g wheel -m 644 \
+# ${.CURDIR}/locate.rc ${DESTDIR}/etc
+
+.include "../../Makefile.inc"
+.include "../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.bin/locate/locate/concatdb.sh b/usr.bin/locate/locate/concatdb.sh
new file mode 100644
index 0000000..f40c7b3
--- /dev/null
+++ b/usr.bin/locate/locate/concatdb.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+#
+# Copyright (c) September 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# concatdb - concatenate locate databases
+#
+# usage: concatdb database1 ... databaseN > newdb
+#
+# Sequence of databases is important.
+#
+# $Id$
+
+# The directory containing locate subprograms
+: ${LIBEXECDIR=/usr/libexec}; export LIBEXECDIR
+
+PATH=$LIBEXECDIR:/bin:/usr/bin:$PATH; export PATH
+
+umask 077 # protect temp files
+
+: ${TMPDIR=/tmp}; export TMPDIR;
+if test X"$TMPDIR" = X -o ! -d "$TMPDIR"; then
+ TMPDIR=/tmp; export TMPDIR
+fi
+
+# utilities to built locate database
+: ${bigram=locate.bigram}
+: ${code=locate.code}
+: ${sort=sort}
+: ${locate=locate}
+
+
+case $# in
+ [01]) echo 'usage: concatdb databases1 ... databaseN > newdb'
+ exit 1
+ ;;
+esac
+
+
+bigrams=$TMPDIR/_concatdb$$.bigrams
+trap 'rm -f $bigrams' 0 1 2 3 5 10 15
+
+for db
+do
+ $locate -d $db /
+done | $bigram | $sort -nr | awk 'NR <= 128 { printf $2 }' > $bigrams
+
+for db
+do
+ $locate -d $db /
+done | $code $bigrams
diff --git a/usr.bin/locate/locate/fastfind.c b/usr.bin/locate/locate/fastfind.c
new file mode 100644
index 0000000..c17c6f1
--- /dev/null
+++ b/usr.bin/locate/locate/fastfind.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+ * 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.
+ *
+ * $Id$
+ */
+
+
+#ifndef _LOCATE_STATISTIC_
+#define _LOCATE_STATISTIC_
+
+void
+statistic (fp, path_fcodes)
+ FILE *fp; /* open database */
+ char *path_fcodes; /* for error message */
+{
+ register int lines, chars, size, big, zwerg;
+ register u_char *p, *s;
+ register int c;
+ int count, umlaut;
+ u_char bigram1[NBG], bigram2[NBG], path[MAXPATHLEN];
+
+ for (c = 0, p = bigram1, s = bigram2; c < NBG; c++) {
+ p[c] = check_bigram_char(getc(fp));
+ s[c] = check_bigram_char(getc(fp));
+ }
+
+ lines = chars = big = zwerg = umlaut = 0;
+ size = NBG + NBG;
+
+ for (c = getc(fp), count = 0; c != EOF; size++) {
+ if (c == SWITCH) {
+ count += getwf(fp) - OFFSET;
+ size += sizeof(int);
+ zwerg++;
+ } else
+ count += c - OFFSET;
+
+ for (p = path + count; (c = getc(fp)) > SWITCH; size++)
+ if (c < PARITY) {
+ if (c == UMLAUT) {
+ c = getc(fp);
+ size++;
+ umlaut++;
+ }
+ p++;
+ } else {
+ /* bigram char */
+ big++;
+ p += 2;
+ }
+
+ p++;
+ lines++;
+ chars += (p - path);
+ }
+
+ (void)printf("\nDatabase: %s\n", path_fcodes);
+ (void)printf("Compression: Front: %2.2f%%, ",
+ (float)(100 * (size + big - (2 * NBG))) / chars);
+ (void)printf("Bigram: %2.2f%%, ", (float)(100 * (size - big)) / size);
+ (void)printf("Total: %2.2f%%\n",
+ (float)(100 * (size - (2 * NBG))) / chars);
+ (void)printf("Filenames: %d, ", lines);
+ (void)printf("Characters: %d, ", chars);
+ (void)printf("Database size: %d\n", size);
+ (void)printf("Bigram characters: %d, ", big);
+ (void)printf("Integers: %d, ", zwerg);
+ (void)printf("8-Bit characters: %d\n", umlaut);
+
+}
+#endif /* _LOCATE_STATISTIC_ */
+
+
+void
+#ifdef FF_MMAP
+
+
+#ifdef FF_ICASE
+fastfind_mmap_icase
+#else
+fastfind_mmap
+#endif /* FF_ICASE */
+(pathpart, paddr, len, database)
+ char *pathpart; /* search string */
+ caddr_t paddr; /* mmap pointer */
+ int len; /* length of database */
+ char *database; /* for error message */
+
+
+#else /* MMAP */
+
+
+#ifdef FF_ICASE
+fastfind_icase
+#else
+fastfind
+#endif /* FF_ICASE */
+
+(fp, pathpart, database)
+ FILE *fp; /* open database */
+ char *pathpart; /* search string */
+ char *database; /* for error message */
+
+
+#endif /* MMAP */
+
+{
+ register u_char *p, *s, *patend, *q, *foundchar;
+ register int c, cc;
+ int count, found, globflag;
+ u_char *cutoff;
+ u_char bigram1[NBG], bigram2[NBG], path[MAXPATHLEN];
+
+#ifdef FF_ICASE
+ /* use a lookup table for case insensitive search */
+ u_char table[UCHAR_MAX + 1];
+
+ tolower_word(pathpart);
+#endif /* FF_ICASE*/
+
+ /* init bigram table */
+#ifdef FF_MMAP
+ if (len < (2*NBG)) {
+ (void)fprintf(stderr, "database too small: %s\n", database);
+ exit(1);
+ }
+
+ for (c = 0, p = bigram1, s = bigram2; c < NBG; c++, len-= 2) {
+ p[c] = check_bigram_char(*paddr++);
+ s[c] = check_bigram_char(*paddr++);
+ }
+#else
+ for (c = 0, p = bigram1, s = bigram2; c < NBG; c++) {
+ p[c] = check_bigram_char(getc(fp));
+ s[c] = check_bigram_char(getc(fp));
+ }
+#endif /* FF_MMAP */
+
+ /* find optimal (last) char for searching */
+ for (p = pathpart; *p != '\0'; p++)
+ if (index(LOCATE_REG, *p) != NULL)
+ break;
+
+ if (*p == '\0')
+ globflag = 0;
+ else
+ globflag = 1;
+
+ p = pathpart;
+ patend = patprep(p);
+ cc = *patend;
+
+#ifdef FF_ICASE
+ /* set patend char to true */
+ for (c = 0; c < UCHAR_MAX + 1; c++)
+ table[c] = 0;
+
+ table[TOLOWER(*patend)] = 1;
+ table[toupper(*patend)] = 1;
+#endif /* FF_ICASE */
+
+
+ /* main loop */
+ found = count = 0;
+ foundchar = 0;
+
+#ifdef FF_MMAP
+ c = (u_char)*paddr++; len--;
+ for (; len > 0; ) {
+#else
+ c = getc(fp);
+ for (; c != EOF; ) {
+#endif /* FF_MMAP */
+
+ /* go forward or backward */
+ if (c == SWITCH) { /* big step, an integer */
+#ifdef FF_MMAP
+ count += getwm(paddr) - OFFSET;
+ len -= INTSIZE; paddr += INTSIZE;
+#else
+ count += getwf(fp) - OFFSET;
+#endif /* FF_MMAP */
+ } else { /* slow step, =< 14 chars */
+ count += c - OFFSET;
+ }
+
+ /* overlay old path */
+ p = path + count;
+ foundchar = p - 1;
+
+ for (;;) {
+#ifdef FF_MMAP
+ c = (u_char)*paddr++;
+ len--;
+#else
+ c = getc(fp);
+#endif /* FF_MMAP */
+ /*
+ * == UMLAUT: 8 bit char followed
+ * <= SWITCH: offset
+ * >= PARITY: bigram
+ * rest: single ascii char
+ *
+ * offset < SWITCH < UMLAUT < ascii < PARITY < bigram
+ */
+ if (c < PARITY) {
+ if (c <= UMLAUT) {
+ if (c == UMLAUT) {
+#ifdef FF_MMAP
+ c = (u_char)*paddr++;
+ len--;
+#else
+ c = getc(fp);
+#endif /* FF_MMAP */
+
+ } else
+ break; /* SWITCH */
+ }
+#ifdef FF_ICASE
+ if (table[c])
+#else
+ if (c == cc)
+#endif /* FF_ICASE */
+ foundchar = p;
+ *p++ = c;
+ }
+ else {
+ /* bigrams are parity-marked */
+ TO7BIT(c);
+
+#ifndef FF_ICASE
+ if (bigram1[c] == cc ||
+ bigram2[c] == cc)
+#else
+
+ if (table[bigram1[c]] ||
+ table[bigram2[c]])
+#endif /* FF_ICASE */
+ foundchar = p + 1;
+
+ *p++ = bigram1[c];
+ *p++ = bigram2[c];
+ }
+ }
+
+ if (found) { /* previous line matched */
+ cutoff = path;
+ *p-- = '\0';
+ foundchar = p;
+ } else if (foundchar >= path + count) { /* a char matched */
+ *p-- = '\0';
+ cutoff = path + count;
+ } else /* nothing to do */
+ continue;
+
+ found = 0;
+ for (s = foundchar; s >= cutoff; s--) {
+ if (*s == cc
+#ifdef FF_ICASE
+ || TOLOWER(*s) == cc
+#endif /* FF_ICASE */
+ ) { /* fast first char check */
+ for (p = patend - 1, q = s - 1; *p != '\0';
+ p--, q--)
+ if (*q != *p
+#ifdef FF_ICASE
+ && TOLOWER(*q) != *p
+#endif /* FF_ICASE */
+ )
+ break;
+ if (*p == '\0') { /* fast match success */
+ found = 1;
+ if (!globflag ||
+#ifndef FF_ICASE
+ !fnmatch(pathpart, path, 0))
+#else
+ !fnmatch(pathpart, path,
+ FNM_CASEFOLD))
+#endif /* !FF_ICASE */
+ {
+ if (f_silent)
+ counter++;
+ else if (f_limit) {
+ counter++;
+ if (f_limit >= counter)
+ (void)puts(path);
+ else {
+ (void)fprintf(stderr, "[show only %d lines]\n", counter - 1);
+ exit(0);
+ }
+ } else
+ (void)puts(path);
+ }
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/usr.bin/locate/locate/locate.1 b/usr.bin/locate/locate/locate.1
new file mode 100644
index 0000000..c580bbe
--- /dev/null
+++ b/usr.bin/locate/locate/locate.1
@@ -0,0 +1,249 @@
+.\" Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" 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
+.\" $Id: locate.1,v 1.8 1997/03/06 05:11:55 jmg Exp $
+.\"
+.Dd June 6, 1993
+.Dt LOCATE 1
+.Os BSD 4.4
+.Sh NAME
+.Nm locate
+.Nd find filenames quickly
+.Sh SYNOPSIS
+.Nm
+.Op Fl Scims
+.Op Fl l Ar limit
+.Op Fl d Ar database
+pattern ...
+.Sh DESCRIPTION
+The
+.Nm
+program searches a database for all pathnames which match the specified
+.Ar pattern .
+The database is recomputed periodically (usually weekly or daily),
+and contains the pathnames
+of all files which are publicly accessible.
+.Pp
+Shell globbing and quoting characters
+.Po
+.Dq * ,
+.Dq ? ,
+.Dq \e ,
+.Dq [
+and
+.Dq \]
+.Pc
+may be used in
+.Ar pattern ,
+although they will have to be escaped from the shell.
+Preceding any character with a backslash
+.Pq Dq \e
+eliminates any special
+meaning which it may have.
+The matching differs in that no characters must be matched explicitly,
+including slashes
+.Pq Dq / .
+.Pp
+As a special case, a pattern containing no globbing characters
+.Pq Dq foo
+is matched as though it were
+.Dq *foo* .
+
+Historically, locate store only characters between 32 and 127. The
+current implementation store any character except newline
+.Pq Sq \en
+and NUL
+.Pq Sq \e0 .
+The 8-bit character support doesn't waste extra space for
+plain ASCII file names. Characters less than 32 or greater than 127
+are stored in 2 bytes.
+
+The following options are available:
+.Bl -tag -width 10n indent
+.It Fl S
+Print some statistic about the database and exit.
+.It Fl c
+Suppress normal output; instead print a count of matching file names.
+.It Fl d Ar database
+Search in
+.Ar database
+instead the default file name database.
+Multiple
+.Fl d
+options are allowed. Each additional
+.Fl d
+option adds the specified database to the list
+of databases to be searched.
+
+The option
+.Ar database
+may be a colon-separated list of databases. A single colon is a reference
+to the default database.
+
+$ locate -d $HOME/lib/mydb: foo
+
+will first search string
+.Dq foo
+in
+.Pa $HOME/lib/mydb
+and then in
+.Pa /var/db/locate.database .
+
+$ locate -d $HOME/lib/mydb::/cdrom/locate.database foo
+
+will first search string
+.Dq foo
+in
+.Pa $HOME/lib/mydb
+and then in
+.Pa /var/db/locate.database
+and then in
+.Pa /cdrom/locate.database .
+
+
+.Do
+$ locate -d db1 -d db2 -d db3 pattern
+.Dc
+is the same as
+
+.Dq $ locate -d db1:db2:db3 pattern
+or
+
+.Dq $ locate -d db1:db2 -d db3 pattern .
+
+If
+.Ar -
+is given as the database name, standard input will be read instead.
+For example, you can compress your database
+and use:
+
+$ zcat database.gz | locate -d - pattern
+
+This might be useful on machines with a fast CPU and little RAM and slow
+I/O. Note: you can only use
+.Ar one
+pattern for stdin.
+.It Fl i
+Ignore case distinctions in both the pattern and the database.
+.It Fl l Ar number
+Limit output to
+.Ar number
+of file names and exit.
+.It Fl m
+Use
+.Xr mmap 2
+instead of the
+.Xr stdio 3
+library. This is the default behavior. Usually faster in most cases.
+.It Fl s
+Use the
+.Xr stdio 3
+library instead of
+.Xr mmap 2 .
+.Sh FILES
+.Bl -tag -width /usr/libexec/locate.updatedb -compact
+.It Pa /var/db/locate.database
+locate database
+.It Pa /usr/libexec/locate.updatedb
+Script to update the locate database
+.It Pa /etc/weekly
+Script that usually starts the database rebuild
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width LOCATE_PATH -compact
+.It Pa LOCATE_PATH
+path to the locate database if set and not empty, ignored if the
+.Fl d
+option was specified.
+.El
+.Sh SEE ALSO
+.Xr find 1 ,
+.Xr fnmatch 3 ,
+.Xr locate.updatedb 8
+.Rs
+.%A Woods, James A.
+.%D 1983
+.%T "Finding Files Fast"
+.%J ";login"
+.%V 8:1
+.%P pp. 8-10
+.Re
+.Sh BUGS
+The
+.Nm
+program may fail to list some files that are present, or may
+list files that have been removed from the system. This is because
+locate only reports files that are present in the database, which is
+typically only regenerated once a week by the
+.Pa /etc/weekly
+script. Use
+.Xr find 1
+to locate files that are of a more transitory nature.
+
+The
+.Nm
+database was built by user
+.Dq nobody .
+.Xr find 1
+skip directories,
+which are not readable for user
+.Dq nobody ,
+group
+.Dq nobody ,
+or
+world. E.g. if your HOME directory ist not world-readable, all your
+files are
+.Ar not
+in the database.
+
+The
+.Nm
+database is not byte order independent. It is not possible
+to share the databases between machines with different byte order.
+The current
+.Nm
+implementation understand databases in host byte order or
+network byte order if both architectures use the same integer size.
+So you can read on a FreeBSD/i386 machine
+(little endian)
+a locate database which was built on SunOS/sparc machine
+(big endian, net).
+
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Bx 4.4 .
+Many new features were
+added in
+.Fx 2.2 .
diff --git a/usr.bin/locate/locate/locate.c b/usr.bin/locate/locate/locate.c
new file mode 100644
index 0000000..a9bed92
--- /dev/null
+++ b/usr.bin/locate/locate/locate.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+ * 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.
+ *
+ * $Id: locate.c,v 1.9 1997/02/22 19:55:47 peter Exp $
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1995-1996 Wolfram Schneider, Berlin.\n\
+@(#) 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
+ * 31 an 8 bit char followed
+ * 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 <ctype.h>
+#include <err.h>
+#include <fnmatch.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef MMAP
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <sys/mman.h>
+# include <fcntl.h>
+#endif
+
+
+#ifdef sun
+#include <netinet/in.h> /* SunOS byteorder(3) htohl(3) */
+#ifndef __P
+#define __P(x) x
+#endif
+#endif
+
+#include "locate.h"
+#include "pathnames.h"
+
+#ifdef DEBUG
+# include <sys/time.h>
+# include <sys/types.h>
+# include <sys/resource.h>
+#endif
+
+char *path_fcodes; /* locate database */
+int f_mmap; /* use mmap */
+int f_icase; /* ignore case */
+int f_stdin; /* read database from stdin */
+int f_statistic; /* print statistic */
+int f_silent; /* suppress output, show only count of matches */
+int f_limit; /* limit number of output lines, 0 == infinite */
+u_int counter; /* counter for matches [-c] */
+
+
+void usage __P((void));
+void statistic __P((FILE *, char *));
+void fastfind __P((FILE *, char *, char *));
+void fastfind_icase __P((FILE *, char *, char *));
+void fastfind_mmap __P((char *, caddr_t, int, char *));
+void fastfind_mmap_icase __P((char *, caddr_t, int, char *));
+void search_mmap __P((char *, char **));
+void search_fopen __P((char *, char **));
+unsigned long cputime __P((void));
+
+extern char **colon __P((char **, char*, char*));
+extern void print_matches __P((u_int));
+extern int getwm __P((caddr_t));
+extern int getwf __P((FILE *));
+extern u_char *tolower_word __P((u_char *));
+extern int check_bigram_char __P((int));
+extern char *patprep __P((char *));
+
+extern char *optarg;
+extern int optind;
+
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register int ch;
+ char **dbv = NULL;
+#ifdef MMAP
+ f_mmap = 1; /* mmap is default */
+#endif
+ (void) setlocale(LC_ALL, "");
+
+ while ((ch = getopt(argc, argv, "Scd:il:ms")) != -1)
+ switch(ch) {
+ case 'S': /* statistic lines */
+ f_statistic = 1;
+ break;
+ case 'l': /* limit number of output lines, 0 == infinite */
+ f_limit = atoi(optarg);
+ break;
+ case 'd': /* database */
+ dbv = colon(dbv, optarg, _PATH_FCODES);
+ break;
+ case 'i': /* ignore case */
+ f_icase = 1;
+ break;
+ case 'm': /* mmap */
+#ifdef MMAP
+ f_mmap = 1;
+#else
+ (void)fprintf(stderr, "mmap(2) not implemented\n");
+#endif
+ break;
+ case 's': /* stdio lib */
+ f_mmap = 0;
+ break;
+ case 'c': /* suppress output, show only count of matches */
+ f_silent = 1;
+ break;
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ /* to few arguments */
+ if (argc < 1 && !(f_statistic))
+ usage();
+
+ /* no (valid) database as argument */
+ if (dbv == NULL || *dbv == NULL) {
+ /* try to read database from enviroment */
+ if ((path_fcodes = getenv("LOCATE_PATH")) == NULL ||
+ *path_fcodes == '\0')
+ /* use default database */
+ dbv = colon(dbv, _PATH_FCODES, _PATH_FCODES);
+ else /* $LOCATE_PATH */
+ dbv = colon(dbv, path_fcodes, _PATH_FCODES);
+ }
+
+ if (f_icase && UCHAR_MAX < 4096) /* init tolower lookup table */
+ for (ch = 0; ch < UCHAR_MAX + 1; ch++)
+ myctype[ch] = tolower(ch);
+
+ /* foreach database ... */
+ while((path_fcodes = *dbv) != NULL) {
+ dbv++;
+
+ if (!strcmp(path_fcodes, "-"))
+ f_stdin = 1;
+ else
+ f_stdin = 0;
+
+#ifndef MMAP
+ f_mmap = 0; /* be paranoid */
+#endif
+ if (!f_mmap || f_stdin || f_statistic)
+ search_fopen(path_fcodes, argv);
+ else
+ search_mmap(path_fcodes, argv);
+ }
+
+ if (f_silent)
+ print_matches(counter);
+ exit(0);
+}
+
+
+void
+search_fopen(db, s)
+ char *db; /* database */
+ char **s; /* search strings */
+{
+ FILE *fp;
+#ifdef DEBUG
+ long t0;
+#endif
+
+ /* can only read stdin once */
+ if (f_stdin) {
+ fp = stdin;
+ if (*(s+1) != NULL) {
+ (void)fprintf(stderr,
+ "read database from stdin, use only");
+ (void)fprintf(stderr, " `%s' as pattern\n", *s);
+ *(s+1) = NULL;
+ }
+ }
+ else if ((fp = fopen(path_fcodes, "r")) == NULL)
+ err(1, "`%s'", path_fcodes);
+
+ /* count only chars or lines */
+ if (f_statistic) {
+ statistic(fp, path_fcodes);
+ (void)fclose(fp);
+ return;
+ }
+
+ /* foreach search string ... */
+ while(*s != NULL) {
+#ifdef DEBUG
+ t0 = cputime();
+#endif
+ if (!f_stdin &&
+ fseek(fp, (long)0, SEEK_SET) == -1)
+ err(1, "fseek to begin of ``%s''\n", path_fcodes);
+
+ if (f_icase)
+ fastfind_icase(fp, *s, path_fcodes);
+ else
+ fastfind(fp, *s, path_fcodes);
+#ifdef DEBUG
+ (void)fprintf(stderr, "fastfind %ld ms\n", cputime () - t0);
+#endif
+ s++;
+ }
+ (void)fclose(fp);
+}
+
+#ifdef MMAP
+void
+search_mmap(db, s)
+ char *db; /* database */
+ char **s; /* search strings */
+{
+ struct stat sb;
+ int fd;
+ caddr_t p;
+ off_t len;
+#ifdef DEBUG
+ long t0;
+#endif
+ if ((fd = open(path_fcodes, O_RDONLY)) == -1 ||
+ fstat(fd, &sb) == -1)
+ err(1, "`%s'", path_fcodes);
+ len = sb.st_size;
+
+ if ((p = mmap((caddr_t)0, (size_t)len,
+ PROT_READ, MAP_SHARED,
+ fd, (off_t)0)) == MAP_FAILED)
+ err(1, "mmap ``%s''", path_fcodes);
+
+ /* foreach search string ... */
+ while (*s != NULL) {
+#ifdef DEBUG
+ t0 = cputime();
+#endif
+ if (f_icase)
+ fastfind_mmap_icase(*s, p, (int)len, path_fcodes);
+ else
+ fastfind_mmap(*s, p, (int)len, path_fcodes);
+#ifdef DEBUG
+ (void)fprintf(stderr, "fastfind %ld ms\n", cputime () - t0);
+#endif
+ s++;
+ }
+
+ if (munmap(p, (size_t)len) == -1)
+ warn("munmap %s\n", path_fcodes);
+
+ (void)close(fd);
+}
+#endif /* MMAP */
+
+#ifdef DEBUG
+unsigned long
+cputime ()
+{
+ struct rusage rus;
+
+ getrusage(0, &rus);
+ return(rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000);
+}
+#endif /* DEBUG */
+
+void
+usage ()
+{
+ (void)fprintf(stderr, "usage: locate [-Scims] [-l limit] ");
+ (void)fprintf(stderr, "[-d database] pattern ...\n\n");
+ (void)fprintf(stderr, "default database: `%s' or $LOCATE_PATH\n",
+ _PATH_FCODES);
+ exit(1);
+}
+
+
+/* load fastfind functions */
+
+/* statistic */
+/* fastfind_mmap, fastfind_mmap_icase */
+#ifdef MMAP
+#undef FF_MMAP
+#undef FF_ICASE
+
+#define FF_MMAP
+#include "fastfind.c"
+#define FF_ICASE
+#include "fastfind.c"
+#endif /* MMAP */
+
+/* fopen */
+/* fastfind, fastfind_icase */
+#undef FF_MMAP
+#undef FF_ICASE
+#include "fastfind.c"
+#define FF_ICASE
+#include "fastfind.c"
diff --git a/usr.bin/locate/locate/locate.h b/usr.bin/locate/locate/locate.h
new file mode 100644
index 0000000..9e997d4
--- /dev/null
+++ b/usr.bin/locate/locate/locate.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+ * 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
+ * $Id$
+ */
+
+/* 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 */
+#define UMLAUT 31 /* an 8 bit char followed */
+
+/* 0-28 likeliest differential counts + offset to make nonnegative */
+#define LDC_MIN 0
+#define LDC_MAX 28
+
+/* 128-255 bigram codes (128 most common, as determined by 'updatedb') */
+#define BIGRAM_MIN (UCHAR_MAX - CHAR_MAX)
+#define BIGRAM_MAX UCHAR_MAX
+
+/* 32-127 single character (printable) ascii residue (ie, literal) */
+#define ASCII_MIN 32
+#define ASCII_MAX CHAR_MAX
+
+/* #define TO7BIT(x) (x = ( ((u_char)x) & CHAR_MAX )) */
+#define TO7BIT(x) (x = x & CHAR_MAX )
+
+
+#if UCHAR_MAX >= 4096
+ define TOLOWER(ch) tolower(ch)
+#else
+
+u_char myctype[UCHAR_MAX + 1];
+#define TOLOWER(ch) (myctype[ch])
+#endif
+
+#define INTSIZE (sizeof(int))
+
+#define LOCATE_REG "*?[]\\" /* fnmatch(3) meta characters */
diff --git a/usr.bin/locate/locate/locate.rc b/usr.bin/locate/locate/locate.rc
new file mode 100644
index 0000000..20937c0
--- /dev/null
+++ b/usr.bin/locate/locate/locate.rc
@@ -0,0 +1,26 @@
+#
+# /etc/locate.rc - command script for updatedb(8)
+#
+# $Id$
+
+#
+# All commented values are the defaults
+#
+# temp directory
+#TMPDIR="/tmp"
+
+# the actual database
+#FCODES="/var/db/locate.database"
+
+# directories to be put in the database
+#SEARCHPATHS="/"
+
+# directories unwanted in output
+#PRUNEPATHS="/tmp /usr/tmp /var/tmp"
+
+# filesystems allowed. Beware: a non-listed filesystem will be pruned
+# and if the SEARCHPATHS starts in such a filesystem locate will build
+# an empty database.
+#
+# be carefully if you add 'nfs'
+#FILESYSTEMS="ufs"
diff --git a/usr.bin/locate/locate/locate.updatedb.8 b/usr.bin/locate/locate/locate.updatedb.8
new file mode 100644
index 0000000..a35c607
--- /dev/null
+++ b/usr.bin/locate/locate/locate.updatedb.8
@@ -0,0 +1,64 @@
+.\" Copyright (c) 1996
+.\" Mike Pritchard <mpp@FreeBSD.org>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Mike Pritchard.
+.\" 4. Neither the name of the author nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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 February 11, 1996
+.Dt LOCATE.UPDATEDB 8
+.Os BSD 4.4
+.Sh NAME
+.Nm locate.updatedb
+.Nd update locate database
+.Sh SYNOPSIS
+.Nm /usr/libexec/locate.updatedb
+.Sh DESCRIPTION
+.Nm Locate.updatedb
+updates the database used by
+.Xr locate 1 .
+It is typically run once a week by the
+.Nm /etc/weekly script.
+.Pp
+The contents of the newly built database can be controlled by the
+.Nm /etc/locate.rc file.
+.Sh FILES
+.Bl -tag -width /var/db/locate.database -compact
+.It Pa /var/db/locate.database
+the actual database
+.It Pa /etc/locate.rc
+the configuration file
+.El
+.Sh SEE ALSO
+.Xr locate 1
+.Rs
+.%A Woods, James A.
+.%D 1983
+.%T "Finding Files Fast"
+.%J ";login"
+.%V 8:1
+.%P pp. 8-10
+.Re
diff --git a/usr.bin/locate/locate/mklocatedb.sh b/usr.bin/locate/locate/mklocatedb.sh
new file mode 100644
index 0000000..e2b4fa7
--- /dev/null
+++ b/usr.bin/locate/locate/mklocatedb.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+#
+# Copyright (c) September 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# mklocatedb - build locate database
+#
+# usage: mklocatedb [-presort] < filelist > database
+#
+# $Id$
+
+
+# The directory containing locate subprograms
+: ${LIBEXECDIR=/usr/libexec}; export LIBEXECDIR
+
+PATH=$LIBEXECDIR:/bin:/usr/bin:$PATH; export PATH
+
+umask 077 # protect temp files
+
+: ${TMPDIR=/tmp}; export TMPDIR;
+if test X"$TMPDIR" = X -o ! -d "$TMPDIR"; then
+ TMPDIR=/tmp; export TMPDIR
+fi
+
+# utilities to built locate database
+: ${bigram=locate.bigram}
+: ${code=locate.code}
+: ${sort=sort}
+
+
+sortopt="-u -T $TMPDIR"
+sortcmd=$sort
+
+# Input already sorted
+case X"$1" in
+ X-nosort|X-presort) sortcmd=cat; sortopt=;shift;;
+esac
+
+
+bigrams=$TMPDIR/_mklocatedb$$.bigrams
+filelist=$TMPDIR/_mklocatedb$$.list
+
+trap 'rm -f $bigrams $filelist' 0 1 2 3 5 10 15
+
+
+if $sortcmd $sortopt > $filelist; then
+ $bigram < $filelist | $sort -nr |
+ awk 'NR <= 128 { printf $2 }' > $bigrams &&
+ $code $bigrams < $filelist
+else
+ echo "`basename $0`: cannot build locate database" >&2
+ exit 1
+fi
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.sh b/usr.bin/locate/locate/updatedb.sh
new file mode 100644
index 0000000..e2622da
--- /dev/null
+++ b/usr.bin/locate/locate/updatedb.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+#
+# Copyright (c) September 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (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 - update locate database for local mounted filesystems
+#
+# $Id$
+
+LOCATE_CONFIG="/etc/locate.rc"
+if [ -f "$LOCATE_CONFIG" -a -r "$LOCATE_CONFIG" ]; then
+ . $LOCATE_CONFIG
+fi
+
+# The directory containing locate subprograms
+: ${LIBEXECDIR=/usr/libexec}; export LIBEXECDIR
+: ${TMPDIR=/tmp}; export TMPDIR
+
+PATH=$LIBEXECDIR:/bin:/usr/bin:$PATH; export PATH
+
+
+: ${mklocatedb=locate.mklocatedb} # make locate database program
+: ${FCODES=/var/db/locate.database} # the database
+: ${SEARCHPATHS="/"} # directories to be put in the database
+: ${PRUNEPATHS="/tmp /usr/tmp /var/tmp"} # unwanted directories
+: ${FILESYSTEMS="ufs"} # allowed filesystems
+: ${find=find}
+
+case X"$SEARCHPATHS" in
+ X) echo "$0: empty variable SEARCHPATHS"; exit 1;; esac
+case X"$FILESYSTEMS" in
+ X) echo "$0: empty variable FILESYSTEMS"; exit 1;; esac
+
+# Make a list a paths to exclude in the locate run
+excludes="! (" or=""
+for fstype in $FILESYSTEMS
+do
+ excludes="$excludes $or -fstype $fstype"
+ or="-or"
+done
+excludes="$excludes ) -prune"
+
+case X"$PRUNEPATHS" in
+ X) ;;
+ *) for path in $PRUNEPATHS
+ do
+ excludes="$excludes -or -path $path -prune"
+ done;;
+esac
+
+tmp=$TMPDIR/_updatedb$$
+trap 'rm -f $tmp' 0 1 2 3 5 10 15
+
+# search locally
+# echo $find $SEARCHPATHS $excludes -or -print && exit
+if $find $SEARCHPATHS $excludes -or -print 2>/dev/null |
+ $mklocatedb > $tmp
+then
+ case X"`$find $tmp -size -257c -print`" in
+ X) cat $tmp > $FCODES;;
+ *) echo "updatedb: locate database $tmp is empty"
+ exit 1
+ esac
+fi
diff --git a/usr.bin/locate/locate/util.c b/usr.bin/locate/locate/util.c
new file mode 100644
index 0000000..57bd157
--- /dev/null
+++ b/usr.bin/locate/locate/util.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+ * 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.
+ *
+ * $Id$
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <sys/param.h>
+#include <stdio.h>
+
+#include "locate.h"
+
+char **colon __P((char **, char*, char*));
+char *patprep __P((char *));
+void print_matches __P((u_int));
+u_char *tolower_word __P((u_char *));
+int getwm __P((caddr_t));
+int getwf __P((FILE *));
+int check_bigram_char __P((int));
+
+/*
+ * Validate bigram chars. If the test failed the database is corrupt
+ * or the database is obviously not a locate database.
+ */
+int
+check_bigram_char(ch)
+ int ch;
+{
+ /* legal bigram: 0, ASCII_MIN ... ASCII_MAX */
+ if (ch == 0 ||
+ (ch >= ASCII_MIN && ch <= ASCII_MAX))
+ return(ch);
+
+ (void)fprintf(stderr, "locate database header corrupt, bigram ");
+ (void)fprintf(stderr, "char outside 0, %d-%d: %d\n",
+ ASCII_MIN, ASCII_MAX, ch);
+ exit(1);
+}
+
+/* split a colon separated string into a char vector
+ *
+ * "bla:foo" -> {"foo", "bla"}
+ * "bla:" -> {"foo", dot}
+ * "bla" -> {"bla"}
+ * "" -> do nothing
+ *
+ */
+char **
+colon(dbv, path, dot)
+ char **dbv;
+ char *path;
+ char *dot; /* default for single ':' */
+{
+ int vlen, slen;
+ char *c, *ch, *p;
+ char **pv;
+
+ if (dbv == NULL) {
+ if ((dbv = malloc(sizeof(char **))) == NULL)
+ err(1, "malloc");
+ *dbv = NULL;
+ }
+
+ /* empty string */
+ if (*path == '\0') {
+ (void)fprintf(stderr, "empty database name, ignored\n");
+ return(dbv);
+ }
+
+ /* length of string vector */
+ for(vlen = 0, pv = dbv; *pv != NULL; pv++, vlen++);
+
+ for (ch = c = path; ; ch++) {
+ if (*ch == ':' ||
+ (!*ch && !(*(ch - 1) == ':' && ch == 1+ path))) {
+ /* single colon -> dot */
+ if (ch == c)
+ p = dot;
+ else {
+ /* a string */
+ slen = ch - c;
+ if ((p = malloc(sizeof(char) * (slen + 1)))
+ == NULL)
+ err(1, "malloc");
+ bcopy(c, p, slen);
+ *(p + slen) = '\0';
+ }
+ /* increase dbv with element p */
+ if ((dbv = realloc(dbv, sizeof(char **) * (vlen + 2)))
+ == NULL)
+ err(1, "realloc");
+ *(dbv + vlen) = p;
+ *(dbv + ++vlen) = NULL;
+ c = ch + 1;
+ }
+ if (*ch == '\0')
+ break;
+ }
+ return (dbv);
+}
+
+void
+print_matches(counter)
+ u_int counter;
+{
+ (void)printf("%d\n", counter);
+}
+
+
+/*
+ * 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'; /* set first element to '\0' */
+ p = name + strlen(name) - 1;
+
+ /* skip trailing metacharacters */
+ for (; p >= name; p--)
+ if (index(LOCATE_REG, *p) == NULL)
+ break;
+
+ /*
+ * check if maybe we are in a character class
+ *
+ * 'foo.[ch]'
+ * |----< p
+ */
+ if (p >= name &&
+ (index(p, '[') != NULL || index(p, ']') != NULL)) {
+ for (p = name; *p != '\0'; p++)
+ if (*p == ']' || *p == '[')
+ break;
+ p--;
+
+ /*
+ * cannot find a non-meta character, give up
+ * '*\*[a-z]'
+ * |-------< p
+ */
+ if (p >= name && index(LOCATE_REG, *p) != NULL)
+ p = name - 1;
+ }
+
+ if (p < name)
+ /* only meta chars: "???", force '/' search */
+ *subp++ = '/';
+
+ else {
+ for (endmark = p; p >= name; p--)
+ if (index(LOCATE_REG, *p) != NULL)
+ break;
+ for (++p;
+ (p <= endmark) && subp < (globfree + sizeof(globfree));)
+ *subp++ = *p++;
+ }
+ *subp = '\0';
+ return(--subp);
+}
+
+/* tolower word */
+u_char *
+tolower_word(word)
+ u_char *word;
+{
+ register u_char *p;
+
+ for(p = word; *p != '\0'; p++)
+ *p = TOLOWER(*p);
+
+ return(word);
+}
+
+
+/*
+ * Read integer from mmap pointer.
+ * Essential a simple ``return *(int *)p'' but avoid sigbus
+ * for integer alignment (SunOS 4.x, 5.x).
+ *
+ * Convert network byte order to host byte order if neccessary.
+ * So we can read on FreeBSD/i386 (little endian) a locate database
+ * which was built on SunOS/sparc (big endian).
+ */
+
+int
+getwm(p)
+ caddr_t p;
+{
+ static char buf[INTSIZE];
+ register int i;
+
+ for (i = 0; i < INTSIZE; i++)
+ buf[i] = *p++;
+
+ i = *(int *)buf;
+
+ if (i > MAXPATHLEN || i < -(MAXPATHLEN)) {
+ i = ntohl(i);
+ if (i > MAXPATHLEN || i < -(MAXPATHLEN)) {
+ (void)fprintf(stderr,
+ "integer out of +-MAXPATHLEN (%d): %d\n",
+ MAXPATHLEN, i);
+ exit(1);
+ }
+ }
+ return(i);
+}
+
+/*
+ * Read integer from stream.
+ *
+ * Convert network byte order to host byte order if neccessary.
+ * So we can read on FreeBSD/i386 (little endian) a locate database
+ * which was built on SunOS/sparc (big endian).
+ */
+
+int
+getwf(fp)
+ FILE *fp;
+{
+ register int word;
+
+ word = getw(fp);
+
+ if (word > MAXPATHLEN || word < -(MAXPATHLEN)) {
+ word = ntohl(word);
+ if (word > MAXPATHLEN || word < -(MAXPATHLEN)) {
+ (void)fprintf(stderr,
+ "integer out of +-MAXPATHLEN (%d): %d\n",
+ MAXPATHLEN, word);
+ exit(1);
+ }
+ }
+ return(word);
+}
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..fba5442
--- /dev/null
+++ b/usr.bin/lock/lock.1
@@ -0,0 +1,71 @@
+.\" 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 n
+.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 n
+Don't use a timeout value. Terminal will be locked forever.
+.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..9f76971
--- /dev/null
+++ b/usr.bin/lock/lock.c
@@ -0,0 +1,243 @@
+/*
+ * 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 */
+int no_timeout; /* lock terminal forever */
+
+/*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;
+ no_timeout = 0;
+ while ((ch = getopt(argc, argv, "npt:")) != -1)
+ 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 'n':
+ no_timeout = 1;
+ break;
+ case '?':
+ default:
+ (void)fprintf(stderr,
+ "usage: lock [-n] [-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;
+ if (!no_timeout)
+ setitimer(ITIMER_REAL, &ntimer, &otimer);
+
+ /* header info */
+ if (no_timeout) {
+(void)printf("lock: %s on %s. no timeout\ntime now is %.20s%s%s",
+ ttynam, hostname, ap, tzn, ap + 19);
+ } else {
+(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. ");
+ if (no_timeout) {
+ (void)putchar('\n');
+ } else {
+ (void)printf("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()
+{
+ if (!no_timeout) {
+ (void)ioctl(0, TIOCSETP, &tty);
+ (void)printf("lock: timeout\n");
+ exit(1);
+ }
+}
diff --git a/usr.bin/lockf/Makefile b/usr.bin/lockf/Makefile
new file mode 100644
index 0000000..47fb6ab
--- /dev/null
+++ b/usr.bin/lockf/Makefile
@@ -0,0 +1,6 @@
+# $Id$
+
+PROG= lockf
+CFLAGS+=-Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/lockf/lockf.1 b/usr.bin/lockf/lockf.1
new file mode 100644
index 0000000..eac10340
--- /dev/null
+++ b/usr.bin/lockf/lockf.1
@@ -0,0 +1,112 @@
+.\"
+.\" Copyright (C) 1997 John D. Polstra. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 JOHN D. POLSTRA AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL JOHN D. POLSTRA OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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 January 8, 1997
+.Os FreeBSD
+.Dt LOCKF 1
+.Sh NAME
+.Nm lockf
+.Nd execute a command while holding a file lock
+.Sh SYNOPSIS
+.Nm
+.Op Fl s
+.Op Fl t Ar seconds
+.Ar file
+.Ar command
+.Op Ar arguments
+.Sh DESCRIPTION
+The
+.Nm
+utility acquires an exclusive lock on a
+.Ar file ,
+creating it if necessary.
+While holding the lock, it executes a
+.Ar command
+with optional
+.Ar arguments .
+After the
+.Ar command
+completes,
+.Nm
+releases the lock and removes the
+.Ar file .
+BSD-style locking is used, as described in
+.Xr flock 2 ;
+the mere existence of the
+.Ar file
+is not considered to constitute a lock.
+.Pp
+The following options are supported:
+.Bl -tag -width Fl
+.It Fl s
+Causes
+.Nm
+to operate silently.
+Failure to acquire the lock is indicated only in the exit status.
+.It Fl t Ar seconds
+Specifies a timeout for waiting for the lock. By default,
+.Nm
+waits indefinitely to acquire the lock.
+If a timeout is specified with this option,
+.Nm
+will wait at most the given number of
+.Ar seconds
+before giving up. A timeout of 0 may be given, in which case
+.Nm
+will fail unless it can acquire the lock immediately.
+.El
+.Pp
+In no event will
+.Nm
+break a lock that is held by another process.
+.Sh DIAGNOSTICS
+If
+.Nm
+successfully acquires the lock, it returns the exit status produced by
+.Ar command .
+Otherwise, it returns one of the exit codes defined in
+.Xr sysexits 3 ,
+as follows:
+.Bl -tag -width F_CANTCREATX
+.It Dv EX_TEMPFAIL
+The specified lock file was already locked by another process.
+.It Dv EX_CANTCREAT
+.Nm
+was unable to create the lock file, e.g., because of insufficient access
+privileges.
+.It Dv EX_USAGE
+There was an error on the
+.Nm
+command line.
+.It Dv EX_OSERR
+A system call (e.g., fork) failed unexpectedly.
+.El
+.Sh SEE ALSO
+.Xr flock 2 ,
+.Xr sysexits 3 .
+.Sh AUTHORS
+John Polstra,
+.Aq jdp@polstra.com .
diff --git a/usr.bin/lockf/lockf.c b/usr.bin/lockf/lockf.c
new file mode 100644
index 0000000..8541ce3
--- /dev/null
+++ b/usr.bin/lockf/lockf.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 1997 John D. Polstra. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 JOHN D. POLSTRA AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JOHN D. POLSTRA OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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: lockf.c,v 1.4 1997/02/22 19:55:54 peter Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+static int acquire_lock(const char *name);
+static void cleanup(void);
+static void killed(int sig);
+static void timeout(int sig);
+static void usage(void);
+static void wait_for_lock(const char *name);
+
+static const char *lockname;
+static volatile sig_atomic_t timed_out;
+
+/*
+ * Execute an arbitrary command while holding a file lock.
+ */
+int
+main(int argc, char **argv)
+{
+ int ch;
+ int lockfd;
+ int silent;
+ int status;
+ int waitsec;
+ pid_t child;
+
+ silent = 0;
+ waitsec = -1; /* Infinite. */
+ while ((ch = getopt(argc, argv, "st:")) != -1) {
+ switch (ch) {
+
+ case 's':
+ silent = 1;
+ break;
+
+ case 't':
+ {
+ char *endptr;
+ waitsec = strtol(optarg, &endptr, 0);
+ if (*optarg == '\0' || *endptr != '\0' || waitsec < 0)
+ errx(EX_USAGE, "invalid timeout \"%s\"", optarg);
+ }
+ break;
+
+ default:
+ usage();
+ }
+ }
+ if (argc - optind < 2)
+ usage();
+ lockname = argv[optind++];
+ argc -= optind;
+ argv += optind;
+
+ if (waitsec > 0) { /* Set up a timeout. */
+ struct sigaction act;
+
+ act.sa_handler = timeout;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0; /* Note that we do not set SA_RESTART. */
+
+ sigaction(SIGALRM, &act, NULL);
+ alarm(waitsec);
+ }
+
+ lockfd = acquire_lock(lockname);
+ while (lockfd == -1 && !timed_out && waitsec != 0) {
+ wait_for_lock(lockname);
+ lockfd = acquire_lock(lockname);
+ }
+
+ if (waitsec > 0)
+ alarm(0);
+
+ if (lockfd == -1) { /* We failed to acquire the lock. */
+ if (silent)
+ exit(EX_TEMPFAIL);
+ errx(EX_TEMPFAIL, "%s: already locked", lockname);
+ }
+
+ /* At this point, we own the lock. */
+
+ if (atexit(cleanup) == -1)
+ err(EX_OSERR, "atexit failed");
+
+ if ((child = fork()) == -1)
+ err(EX_OSERR, "cannot fork");
+
+ if (child == 0) { /* The child process. */
+ close(lockfd);
+ execvp(argv[0], argv);
+ perror(argv[0]);
+ _exit(1);
+ }
+
+ /* This is the parent process. */
+
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, killed);
+
+ if (waitpid(child, &status, 0) == -1)
+ err(EX_OSERR, "waitpid failed");
+
+ return WIFEXITED(status) ? WEXITSTATUS(status) : 1;
+}
+
+/*
+ * Try to acquire a lock on the given file, but don't wait for it. Returns
+ * an open file descriptor on success, or -1 on failure.
+ */
+static int
+acquire_lock(const char *name)
+{
+ int fd;
+
+ if ((fd = open(name, O_RDONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 0666)) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ return -1;
+ err(EX_CANTCREAT, "cannot open %s", name);
+ }
+ return fd;
+}
+
+/*
+ * Remove the lock file.
+ */
+static void
+cleanup(void)
+{
+ unlink(lockname);
+}
+
+/*
+ * Signal handler for SIGTERM. Cleans up the lock file, then re-raises
+ * the signal.
+ */
+static void
+killed(int sig)
+{
+ cleanup();
+ signal(sig, SIG_DFL);
+ if (kill(getpid(), sig) == -1)
+ err(EX_OSERR, "kill failed");
+}
+
+/*
+ * Signal handler for SIGALRM.
+ */
+static void
+timeout(int sig)
+{
+ timed_out = 1;
+}
+
+static void
+usage(void)
+{
+ errx(EX_USAGE, "usage: lockf [-s] [-t seconds] file command [arguments]");
+}
+
+/*
+ * Wait until it might be possible to acquire a lock on the given file.
+ */
+static void
+wait_for_lock(const char *name)
+{
+ int fd;
+
+ if ((fd = open(name, O_RDONLY|O_EXLOCK)) == -1) {
+ if (errno == ENOENT || errno == EINTR)
+ return;
+ err(EX_CANTCREAT, "cannot open %s", name);
+ }
+ close(fd);
+ return;
+}
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..553591f
--- /dev/null
+++ b/usr.bin/logger/logger.c
@@ -0,0 +1,193 @@
+/*
+ * 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;
+ unsetenv("TZ");
+ while ((ch = getopt(argc, argv, "f:ip:st:")) != -1)
+ 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..9ded00c
--- /dev/null
+++ b/usr.bin/login/Makefile
@@ -0,0 +1,33 @@
+# From: @(#)Makefile 8.1 (Berkeley) 7/19/93
+# $Id$
+
+PROG= login
+MAN1= login.1
+MAN5= login.access.5
+SRCS= login.c login_access.c login_fbtab.c
+
+#Uncomment to activate login_auth
+#Warning: requires src/libexec/login_* auth modules
+#LC_AUTH=-DLOGIN_CAP_AUTH
+CFLAGS+=-DSKEY -DLOGIN_ACCESS -DLOGALL -DLOGIN_CAP $(LC_AUTH)
+
+.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) && !defined(LC_AUTH)
+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..4263786
--- /dev/null
+++ b/usr.bin/login/klogin.c
@@ -0,0 +1,204 @@
+/*-
+ * 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 <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;
+ char realm[REALM_SZ], savehost[MAXHOSTNAMELEN];
+ char tkt_location[MAXPATHLEN];
+ char *krb_get_phost();
+ extern int noticketsdontcomplain;
+
+#ifdef KLOGIN_PARANOID
+ AUTH_DAT authdata;
+ KTEXT_ST ticket;
+ struct hostent *hp;
+ unsigned long faddr;
+
+ 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..e0a4f02
--- /dev/null
+++ b/usr.bin/login/login.1
@@ -0,0 +1,179 @@
+.\" 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
+.\" $Id$
+.\"
+.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
+displays 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/login.access
+exists,
+.Nm login
+checks to see if the user and host pair are specifically allowed or denied
+access.
+Login access may also be controlled via the login class, which provides
+allow and deny records based on time, tty and remote host name.
+.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).
+Other environment variables may be set due to entries in the login
+class capabilities database, for the login class assigned in the
+user's system passwd record.
+The login class also controls the maximum and current process resource
+limits granted to a login, process priorities and many other aspects of
+a user's login environment.
+.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/login.conf
+login class capabilities database
+.It Pa /etc/motd
+message-of-the-day
+.It Pa /etc/nologin
+disallows logins
+.It Pa /etc/login.access
+login access control table
+.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 login.access 5 ,
+.Xr login.conf 5 ,
+.Xr utmp 5 ,
+.Xr environ 7 ,
+.Xr nologin 8
+.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..201c185
--- /dev/null
+++ b/usr.bin/login/login.access.5
@@ -0,0 +1,50 @@
+.\" this is comment
+.Dd April 30, 1994
+.Dt LOGIN.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..77edbbf
--- /dev/null
+++ b/usr.bin/login/login.c
@@ -0,0 +1,997 @@
+/*-
+ * 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.
+ */
+
+#if 0
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#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/copyright.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <netdb.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 LOGIN_CAP
+#include <login_cap.h>
+#else /* Undef AUTH as well */
+#undef LOGIN_CAP_AUTH
+#endif
+
+/*
+ * If LOGIN_CAP_AUTH is activated:
+ * kerberose & skey logins are runtime selected via login
+ * login_getstyle() and authentication types for login classes
+ * The actual login itself is handled via /usr/libexec/login_<style>
+ * Valid styles are determined by the auth-type=style,style entries
+ * in the login class.
+ */
+#ifdef LOGIN_CAP_AUTH
+#undef KERBEROS
+#undef SKEY
+#endif /* LOGIN_CAP_AUTH */
+
+#ifdef SKEY
+#include <skey.h>
+#endif /* SKEY */
+
+#include "pathnames.h"
+
+void badlogin __P((char *));
+void checknologin __P((void));
+void dolastlog __P((int));
+void getloginname __P((void));
+void motd __P((char *));
+int rootterm __P((char *));
+void sigint __P((int));
+void sleepexit __P((int));
+void refused __P((char *,char *,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 */
+#define DEFAULT_BACKOFF 3
+#define DEFAULT_RETRIES 10
+
+/*
+ * 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, *envinit[1], *hostname, *username, *tty;
+char full_hostname[MAXHOSTNAMELEN];
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char **environ;
+ struct group *gr;
+ struct stat st;
+ struct timeval tp;
+ struct utmp utmp;
+ int rootok, retries, backoff;
+ int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval;
+ int changepass;
+ time_t warntime;
+ uid_t uid;
+ char *domain, *p, *ep, *salt, *ttyn;
+ char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
+ char localhost[MAXHOSTNAMELEN];
+ char *shell = NULL;
+#ifdef LOGIN_CAP
+ login_cap_t *lc = NULL;
+#ifdef LOGIN_CAP_AUTH
+ char *style, *authtype;
+ char *auth_method = NULL;
+ char *instance = NULL;
+ int authok;
+#endif /* LOGIN_CAP_AUTH */
+#endif /* LOGIN_CAP */
+#ifdef SKEY
+ int permit_passwd = 0;
+#endif /* SKEY */
+
+ (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;
+ term = 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")) != -1)
+ 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;
+ if (strlen(optarg) > UT_HOSTSIZE) {
+ struct hostent *hp = gethostbyname(optarg);
+
+ if (hp != NULL) {
+ struct in_addr in;
+
+ memmove(&in, hp->h_addr, sizeof(in));
+ optarg = strdup(inet_ntoa(in));
+ } else
+ optarg = "invalid hostname";
+ }
+ 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, '/')) != NULL)
+ ++tty;
+ else
+ tty = ttyn;
+
+#ifdef LOGIN_CAP_AUTH
+ authtype = hostname ? "rlogin" : "login";
+#endif
+#ifdef LOGIN_CAP
+ /*
+ * Get "login-retries" & "login-backoff" from default class
+ */
+ lc = login_getclass(NULL);
+ retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES, DEFAULT_RETRIES);
+ backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF, DEFAULT_BACKOFF);
+ login_close(lc);
+ lc = NULL;
+#else
+ retries = DEFAULT_RETRIES;
+ backoff = DEFAULT_BACKOFF;
+#endif
+
+ for (cnt = 0;; ask = 1) {
+ if (ask) {
+ fflag = 0;
+ getloginname();
+ }
+ rootlogin = 0;
+ rootok = rootterm(tty); /* Default (auth may change) */
+#ifdef LOGIN_CAP_AUTH
+ authok = 0;
+ if (auth_method = strchr(username, ':')) {
+ *auth_method = '\0';
+ auth_method++;
+ if (*auth_method == '\0')
+ auth_method = NULL;
+ }
+ /*
+ * We need to do this regardless of whether
+ * kerberos is available.
+ */
+ if ((instance = strchr(username, '.')) != NULL) {
+ if (strncmp(instance, ".root", 5) == 0)
+ rootlogin = 1;
+ *instance++ = '\0';
+ } else
+ instance = "";
+#else /* !LOGIN_CAP_AUTH */
+#ifdef KERBEROS
+ if ((instance = strchr(username, '.')) != NULL) {
+ if (strncmp(instance, ".root", 5) == 0)
+ rootlogin = 1;
+ *instance++ = '\0';
+ } else
+ instance = "";
+#endif /* KERBEROS */
+#endif /* LOGIN_CAP_AUTH */
+
+ 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)) != NULL)
+ salt = pwd->pw_passwd;
+ else
+ salt = "xx";
+
+#ifdef LOGIN_CAP
+ /*
+ * Establish the class now, before we might goto
+ * within the next block. pwd can be NULL since it
+ * falls back to the "default" class if it is.
+ */
+ lc = login_getpwclass(pwd);
+#endif /* LOGIN_CAP */
+
+ /*
+ * 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.
+ */
+ rval = 1;
+ if (pwd != NULL) {
+ if (pwd->pw_uid == 0)
+ rootlogin = 1;
+
+ if (fflag && (uid == (uid_t)0 ||
+ uid == (uid_t)pwd->pw_uid)) {
+ /* already authenticated */
+ break;
+ } else if (pwd->pw_passwd[0] == '\0') {
+ if (!rootlogin || rootok) {
+ /* pretend password okay */
+ rval = 0;
+ goto ttycheck;
+ }
+ }
+ }
+
+ fflag = 0;
+
+ (void)setpriority(PRIO_PROCESS, 0, -4);
+
+#ifdef LOGIN_CAP_AUTH
+ /*
+ * This hands off authorization to an authorization program,
+ * depending on the styles available for the "auth-login",
+ * auth-rlogin (or default) authorization styles.
+ * We do this regardless of whether an account exists so that
+ * the remote user cannot tell a "real" from an invented
+ * account name. If we don't have an account we just fall
+ * back to the first method for the "default" class.
+ */
+ if (!(style = login_getstyle(lc, auth_method, authtype))) {
+
+ /*
+ * No available authorization method
+ */
+ rval = 1;
+ (void)printf("No auth method available for %s.\n",
+ authtype);
+ } else {
+
+ /*
+ * Put back the kerberos instance, if any was given.
+ * Don't worry about the non-kerberos case here, since
+ * if kerberos is not available or not selected and an
+ * instance is given at the login prompt, su or rlogin -l,
+ * then anything else should fail as well.
+ */
+ if (*instance)
+ *(instance - 1) = '.';
+
+ rval = authenticate(username,
+ lc ? lc->lc_class : "default",
+ style, authtype);
+ /* Junk it again */
+ if (*instance)
+ *(instance - 1) = '\0';
+ }
+
+ if (!rval) {
+ char * approvp;
+
+ /*
+ * If authentication succeeds, run any approval
+ * program, if applicable for this class.
+ */
+ approvep = login_getcapstr(lc, "approve", NULL, NULL);
+ rval = 1; /* Assume bad login again */
+
+ if (approvep==NULL ||
+ auth_script(approvep, approvep, username,
+ lc->lc_class, 0) == 0) {
+ int r;
+
+ r = auth_scan(AUTH_OKAY);
+ /*
+ * See what the authorize program says
+ */
+ if (r != AUTH_NONE) {
+ rval = 0;
+
+ if (!rootok && (r & AUTH_ROOTOKAY))
+ rootok = 1; /* root approved */
+ else
+ rootlogin = 0;
+
+ if (!authok && (r & AUTH_SECURE))
+ authok = 1; /* secure */
+ }
+ }
+ }
+#else /* !LOGIN_CAP_AUTH */
+#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 /* !SKEY */
+ p = getpass("Password:");
+ ep = crypt(p, salt);
+#endif/* SKEY */
+
+ 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 /* SKEY */
+ rval = klogin(pwd, instance, localhost, p);
+ if (rval != 0 && rootlogin && pwd->pw_uid != 0)
+ rootlogin = 0;
+ if (rval == 0)
+ authok = 1; /* kerberos authenticated ok */
+ else if (rval == 1) /* fallback to unix passwd */
+ rval = strcmp(ep, pwd->pw_passwd);
+#else /* !KERBEROS */
+ rval = strcmp(ep, pwd->pw_passwd);
+#endif /* KERBEROS */
+ }
+
+ /* clear entered password */
+ memset(p, 0, strlen(p));
+#endif /* LOGIN_CAP_AUTH */
+
+ (void)setpriority(PRIO_PROCESS, 0, 0);
+
+#ifdef LOGIN_CAP_AUTH
+ if (rval)
+ auth_rmfiles();
+#endif
+ ttycheck:
+ /*
+ * If trying to log in as root without Kerberos,
+ * but with insecure terminal, refuse the login attempt.
+ */
+ if (pwd && !rval) {
+#if defined(KERBEROS) || defined(LOGIN_CAP_AUTH)
+ if (authok == 0 && rootlogin && !rootok)
+#else
+ if (rootlogin && !rootok)
+#endif
+ refused(NULL, "NOROOT", 0);
+ else /* valid password & authenticated */
+ break;
+ }
+
+ (void)printf("Login incorrect\n");
+ failures++;
+
+ /*
+ * we allow up to 'retry' (10) tries,
+ * but after 'backoff' (3) we start backing off
+ */
+ if (++cnt > backoff) {
+ if (cnt >= retries) {
+ 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 */
+#ifdef LOGIN_CAP
+ if (!rootlogin)
+ auth_checknologin(lc);
+#else
+ if (!rootlogin)
+ checknologin();
+#endif
+
+#ifdef LOGIN_CAP
+ quietlog = login_getcapbool(lc, "hushlogin", 0);
+#else
+ quietlog = 0;
+#endif
+ if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
+#ifdef LOGIN_CAP
+ if (login_getcapbool(lc, "requirehome", 0))
+ refused("Home directory not available", "HOMEDIR", 1);
+#endif
+ if (chdir("/") < 0)
+ refused("Cannot find root directory", "ROOTDIR", 1);
+ pwd->pw_dir = "/";
+ if (!quietlog || *pwd->pw_dir)
+ printf("No home directory.\nLogging in with home = \"/\".\n");
+ }
+ if (!quietlog)
+ quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
+
+ if (pwd->pw_change || pwd->pw_expire)
+ (void)gettimeofday(&tp, (struct timezone *)NULL);
+
+#define DEFAULT_WARN (2L * 7L & 86400L) /* Two weeks */
+
+#ifdef LOGIN_CAP
+ warntime = login_getcaptime(lc, "warnpassword",
+ DEFAULT_WARN, DEFAULT_WARN);
+#else
+ warntime = DEFAULT_WARN;
+#endif
+
+ changepass=0;
+ if (pwd->pw_change) {
+ if (tp.tv_sec >= pwd->pw_change) {
+ (void)printf("Sorry -- your password has expired.\n");
+ changepass=1;
+ syslog(LOG_INFO,
+ "%s Password expired - forcing change",
+ pwd->pw_name);
+ } else if (pwd->pw_change - tp.tv_sec < warntime && !quietlog)
+ (void)printf("Warning: your password expires on %s",
+ ctime(&pwd->pw_change));
+ }
+
+#ifdef LOGIN_CAP
+ warntime = login_getcaptime(lc, "warnexpire",
+ DEFAULT_WARN, DEFAULT_WARN);
+#else
+ warntime = DEFAULT_WARN;
+#endif
+
+ if (pwd->pw_expire) {
+ if (tp.tv_sec >= pwd->pw_expire) {
+ refused("Sorry -- your account has expired",
+ "EXPIRED", 1);
+ } else if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog)
+ (void)printf("Warning: your account expires on %s",
+ ctime(&pwd->pw_expire));
+ }
+
+#ifdef LOGIN_CAP
+ if (lc != NULL) {
+ if (hostname) {
+ struct hostent *hp = gethostbyname(full_hostname);
+
+ if (hp == NULL)
+ optarg = NULL;
+ else {
+ struct in_addr in;
+ memmove(&in, hp->h_addr, sizeof(in));
+ optarg = strdup(inet_ntoa(in));
+ }
+ if (!auth_hostok(lc, full_hostname, optarg))
+ refused("Permission denied", "HOST", 1);
+ }
+
+ if (!auth_ttyok(lc, tty))
+ refused("Permission denied", "TTY", 1);
+
+ if (!auth_timeok(lc, time(NULL)))
+ refused("Logins not available right now", "TIME", 1);
+ }
+ shell=login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
+#else /* !LOGIN_CAP */
+ shell=pwd->pw_shell;
+#endif /* LOGIN_CAP */
+ if (*pwd->pw_shell == '\0')
+ pwd->pw_shell = _PATH_BSHELL;
+ if (*shell == '\0') /* Not overridden */
+ shell = pwd->pw_shell;
+ if ((shell = strdup(shell)) == NULL) {
+ syslog(LOG_NOTICE, "memory allocation error");
+ sleepexit(1);
+ }
+
+#ifdef LOGIN_ACCESS
+ if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0)
+ refused("Permission denied", "ACCESS", 1);
+#endif /* LOGIN_ACCESS */
+
+ /* 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);
+
+ /*
+ * Preserve TERM if it happens to be already set.
+ */
+ if ((term = getenv("TERM")) != NULL)
+ term = strdup(term);
+
+ /*
+ * Exclude cons/vt/ptys only, assume dialup otherwise
+ * TODO: Make dialup tty determination a library call
+ * for consistency (finger etc.)
+ */
+ if (hostname==NULL && isdialuptty(tty))
+ syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
+
+#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 on %s as %s",
+ full_hostname, tty, pwd->pw_name);
+ else
+ syslog(LOG_INFO, "login on %s as %s",
+ tty, pwd->pw_name);
+#endif
+
+ /*
+ * 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, full_hostname);
+ else
+ syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
+ username, tty);
+ }
+
+ /*
+ * Destroy environment unless user has requested its preservation.
+ * We need to do this before setusercontext() because that may
+ * set or reset some environment variables.
+ */
+ if (!pflag)
+ environ = envinit;
+
+ /*
+ * We don't need to be root anymore, so
+ * set the user and session context
+ */
+#ifdef LOGIN_CAP
+ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL) != 0) {
+ syslog(LOG_ERR, "setusercontext() failed - exiting");
+ exit(1);
+ }
+#else
+ if (setlogin(pwd->pw_name) < 0)
+ syslog(LOG_ERR, "setlogin() failure: %m");
+
+ (void)setgid(pwd->pw_gid);
+ initgroups(username, pwd->pw_gid);
+ (void)setuid(rootlogin ? 0 : pwd->pw_uid);
+#endif
+
+ (void)setenv("SHELL", pwd->pw_shell, 1);
+ (void)setenv("HOME", pwd->pw_dir, 1);
+ if (term != NULL && *term != '\0')
+ (void)setenv("TERM", term, 1); /* Preset overrides */
+ else {
+ (void)setenv("TERM", stypeof(tty), 0); /* Fallback doesn't */
+ }
+ (void)setenv("LOGNAME", pwd->pw_name, 1);
+ (void)setenv("USER", pwd->pw_name, 1);
+ (void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
+#ifdef KERBEROS
+ if (krbtkfile_env)
+ (void)setenv("KRBTKFILE", krbtkfile_env, 1);
+#endif
+#if LOGIN_CAP_AUTH
+ auth_env();
+#endif
+
+#ifdef LOGIN_CAP
+ if (!quietlog) {
+ char *cw;
+
+ cw = login_getcapstr(lc, "copyright", NULL, NULL);
+ if (cw != NULL && access(cw, F_OK) == 0)
+ motd(cw);
+ else
+ (void)printf("%s\n\t%s %s\n",
+ "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
+ "The Regents of the University of California. ",
+ "All rights reserved.");
+
+ (void)printf("\n");
+
+ cw = login_getcapstr(lc, "welcome", NULL, NULL);
+ if (cw == NULL || access(cw, F_OK) != 0)
+ cw = _PATH_MOTDFILE;
+ motd(cw);
+
+ cw = getenv("MAIL"); /* $MAIL may have been set by class */
+ if (cw != NULL) {
+ strncpy(tbuf, cw, sizeof(tbuf));
+ tbuf[sizeof(tbuf)-1] = '\0';
+ } else
+ snprintf(tbuf, sizeof(tbuf), "%s/%s",
+ _PATH_MAILDIR, pwd->pw_name);
+#else
+ if (!quietlog) {
+ (void)printf("%s\n\t%s %s\n",
+ "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
+ "The Regents of the University of California. ",
+ "All rights reserved.");
+ motd(_PATH_MOTDFILE);
+ snprintf(tbuf, sizeof(tbuf), "%s/%s",
+ _PATH_MAILDIR, pwd->pw_name);
+#endif
+ if (stat(tbuf, &st) == 0 && st.st_size != 0)
+ (void)printf("You have %smail.\n",
+ (st.st_mtime > st.st_atime) ? "new " : "");
+ }
+
+#ifdef LOGIN_CAP
+ login_close(lc);
+#endif
+
+ (void)signal(SIGALRM, SIG_DFL);
+ (void)signal(SIGQUIT, SIG_DFL);
+ (void)signal(SIGINT, SIG_DFL);
+ (void)signal(SIGTSTP, SIG_IGN);
+
+ if (changepass) {
+ if (system(_PATH_CHPASS) != 0)
+ sleepexit(1);
+ }
+
+ /*
+ * Login shells have a leading '-' in front of argv[0]
+ */
+ tbuf[0] = '-';
+ (void)strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell);
+
+ execlp(shell, tbuf, 0);
+ err(1, "%s", shell);
+}
+
+
+/*
+ * Allow for authentication style and/or kerberos instance
+ * */
+
+#define NBUFSIZ UT_NAMESIZE + 64
+
+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);
+}
+
+volatile int motdinterrupt;
+
+/* ARGSUSED */
+void
+sigint(signo)
+ int signo;
+{
+ motdinterrupt = 1;
+}
+
+void
+motd(motdfile)
+ char *motdfile;
+{
+ int fd, nchars;
+ sig_t oldint;
+ char tbuf[256];
+
+ if ((fd = open(motdfile, O_RDONLY, 0)) < 0)
+ return;
+ motdinterrupt = 0;
+ oldint = signal(SIGINT, sigint);
+ while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 && !motdinterrupt)
+ (void)write(fileno(stdout), tbuf, nchars);
+ (void)signal(SIGINT, oldint);
+ (void)close(fd);
+}
+
+/* ARGSUSED */
+void
+timedout(signo)
+ int signo;
+{
+ (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
+ exit(0);
+}
+
+#ifndef LOGIN_CAP
+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);
+ }
+}
+#endif
+
+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" : "", full_hostname);
+ syslog(LOG_AUTHPRIV|LOG_NOTICE,
+ "%d LOGIN FAILURE%s FROM %s, %s",
+ failures, failures > 1 ? "S" : "", full_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
+refused(msg, rtype, lout)
+ char *msg;
+ char *rtype;
+ int lout;
+{
+
+ if (msg != NULL)
+ printf("%s.\n", msg);
+ if (hostname)
+ syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
+ pwd->pw_name, rtype, full_hostname, tty);
+ else
+ syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
+ pwd->pw_name, rtype, tty);
+ if (lout)
+ sleepexit(1);
+}
+
+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..b5003c7
--- /dev/null
+++ b/usr.bin/login/login_access.c
@@ -0,0 +1,239 @@
+ /*
+ * 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 */
+
+int
+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")) != NULL) {
+ 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)) != NULL) /* 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)
+gid_t group;
+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");
+ return 0;
+#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)) != NULL) {/* 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/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..d8de2ce
--- /dev/null
+++ b/usr.bin/logname/logname.1
@@ -0,0 +1,77 @@
+.\" 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
+.\" $Id: logname.1,v 1.3 1997/04/27 08:45:45 jmg Exp $
+.\"
+.Dd June 9, 1993
+.Dt LOGNAME 1
+.Os BSD 4.4
+.Sh NAME
+.Nm logname
+.Nd display user's login name
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility writes the user's login name to standard output followed by
+a newline.
+.Pp
+The
+.Nm
+utility explicitly ignores the
+.Ev LOGNAME
+and
+.Ev USER
+environment variables
+because the environment cannot be trusted.
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr who 1 ,
+.Xr whoami 1 ,
+.Xr getlogin 2
+.Sh STANDARDS
+The
+.Nm
+function is expected to conform to
+.St -p1003.2 .
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/usr.bin/whereis/whereis.c b/usr.bin/logname/logname.c
index 4e39cda..c0f016e 100644
--- a/usr.bin/whereis/whereis.c
+++ b/usr.bin/logname/logname.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 1993
+ * 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
@@ -33,24 +33,20 @@
#ifndef lint
static char copyright[] =
-"@(#) Copyright (c) 1993\n\
+"@(#) 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[] = "@(#)whereis.c 8.3 (Berkeley) 5/4/95";
+static char sccsid[] = "@(#)logname.c 8.2 (Berkeley) 4/3/94";
#endif /* not lint */
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/sysctl.h>
-
#include <err.h>
#include <errno.h>
+#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
void usage __P((void));
@@ -59,12 +55,10 @@ main(argc, argv)
int argc;
char *argv[];
{
- struct stat sb;
- size_t len;
- int ch, sverrno, mib[2];
- char *p, *t, *std, path[MAXPATHLEN];
+ int ch;
+ char *p;
- while ((ch = getopt(argc, argv, "")) != EOF)
+ while ((ch = getopt(argc, argv, "")) != -1)
switch (ch) {
case '?':
default:
@@ -73,48 +67,15 @@ main(argc, argv)
argc -= optind;
argv += optind;
- if (argc == 0)
- usage();
-
- /* 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)
+ if ((p = getlogin()) == 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)printf("%s\n", p);
+ exit(0);
}
void
usage()
{
-
- (void)fprintf(stderr, "usage: whereis program [...]\n");
- exit (1);
+ (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..be6f666
--- /dev/null
+++ b/usr.bin/look/look.1
@@ -0,0 +1,105 @@
+.\" 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
+.At v7 .
diff --git a/usr.bin/look/look.c b/usr.bin/look/look.c
new file mode 100644
index 0000000..c3b7480
--- /dev/null
+++ b/usr.bin/look/look.c
@@ -0,0 +1,363 @@
+/*-
+ * 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.2 (Berkeley) 5/4/95";
+#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 <locale.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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) (isupper(c) ? tolower(c) : (unsigned char) (c))
+#define DICT(c) (isalnum(c) ? (c) & 0xFF /* int */ : NO_COMPARE)
+
+int dflag, fflag;
+
+char *binary_search __P((unsigned char *, unsigned char *, unsigned char *));
+int compare __P((unsigned char *, unsigned char *, unsigned char *));
+void err __P((const char *fmt, ...));
+char *linear_search __P((unsigned char *, unsigned char *, unsigned char *));
+int look __P((unsigned char *, unsigned char *, unsigned char *));
+void print_from __P((unsigned char *, unsigned char *, unsigned char *));
+
+static void usage __P((void));
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct stat sb;
+ int ch, fd, termchar;
+ unsigned char *back, *file, *front, *string, *p;
+
+ (void) setlocale(LC_CTYPE, "");
+
+ file = _PATH_WORDS;
+ termchar = '\0';
+ while ((ch = getopt(argc, argv, "dft:")) != -1)
+ 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, MAP_SHARED, fd, (off_t)0)) == MAP_FAILED)
+ err("%s: %s", file, strerror(errno));
+ back = front + sb.st_size;
+ exit(look(string, front, back));
+}
+
+look(string, front, back)
+ unsigned char *string, *front, *back;
+{
+ register int ch;
+ register unsigned 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 unsigned char *string, *front, *back;
+{
+ register unsigned 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)
+ unsigned 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 unsigned 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 unsigned 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..c99e611
--- /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..82a4798
--- /dev/null
+++ b/usr.bin/lsvfs/lsvfs.1
@@ -0,0 +1,55 @@
+.\" $Id$
+.\" 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
+command 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..ce63d0e
--- /dev/null
+++ b/usr.bin/lsvfs/lsvfs.c
@@ -0,0 +1,99 @@
+/*
+ * lsvfs - lsit loaded VFSes
+ * Garrett A. Wollman, September 1994
+ * This file is in the public domain.
+ *
+ * $Id: lsvfs.c,v 1.7 1997/02/22 19:55:59 peter Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <string.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, unicode"];
+ 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");
+ }
+
+ if(flags & VFCF_UNICODE) {
+ if(comma++) strcat(buf, ", ");
+ strcat(buf, "unicode");
+ }
+
+ 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..c60c912
--- /dev/null
+++ b/usr.bin/m4/PSD.doc/Makefile
@@ -0,0 +1,11 @@
+# $OpenBSD: Makefile,v 1.2 1996/06/26 05:36:17 deraadt Exp $
+
+
+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..ef0b5ef
--- /dev/null
+++ b/usr.bin/m4/TEST/ack.m4
@@ -0,0 +1,42 @@
+# $OpenBSD: ack.m4,v 1.2 1996/06/26 05:36:18 deraadt Exp $
+# $NetBSD: ack.m4,v 1.4 1995/09/28 05:37:54 tls Exp $
+#
+# 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..d16f922
--- /dev/null
+++ b/usr.bin/m4/TEST/hanoi.m4
@@ -0,0 +1,47 @@
+# $OpenBSD: hanoi.m4,v 1.2 1996/06/26 05:36:19 deraadt Exp $
+# $NetBSD: hanoi.m4,v 1.4 1995/09/28 05:37:56 tls Exp $
+#
+# 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..21b40e1
--- /dev/null
+++ b/usr.bin/m4/TEST/hash.m4
@@ -0,0 +1,57 @@
+# $OpenBSD: hash.m4,v 1.2 1996/06/26 05:36:19 deraadt Exp $
+# $NetBSD: hash.m4,v 1.4 1995/09/28 05:37:58 tls Exp $
+#
+# 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..d01789b
--- /dev/null
+++ b/usr.bin/m4/TEST/sqroot.m4
@@ -0,0 +1,47 @@
+# $OpenBSD: sqroot.m4,v 1.2 1996/06/26 05:36:20 deraadt Exp $
+# $NetBSD: sqroot.m4,v 1.4 1995/09/28 05:38:01 tls Exp $
+#
+# 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..bb0bba4
--- /dev/null
+++ b/usr.bin/m4/TEST/string.m4
@@ -0,0 +1,47 @@
+# $OpenBSD: string.m4,v 1.2 1996/06/26 05:36:20 deraadt Exp $
+# $NetBSD: string.m4,v 1.4 1995/09/28 05:38:03 tls Exp $
+#
+# 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..1c77b9b
--- /dev/null
+++ b/usr.bin/m4/TEST/test.m4
@@ -0,0 +1,245 @@
+# $OpenBSD: test.m4,v 1.2 1996/06/26 05:36:21 deraadt Exp $
+# $NetBSD: test.m4,v 1.4 1995/09/28 05:38:05 tls Exp $
+#
+# 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..be9c1c0
--- /dev/null
+++ b/usr.bin/m4/eval.c
@@ -0,0 +1,803 @@
+/*
+ * 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;
+ case '@':
+ for( n = argc - 1; n >= 2; n-- )
+ {
+ putback(rquote);
+ pbstr(argv[n]);
+ putback(lquote);
+ if( n > 2 )
+ putback(',');
+ }
+ 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..ef2e658
--- /dev/null
+++ b/usr.bin/m4/m4.1
@@ -0,0 +1,181 @@
+.\"
+.\" @(#) $Id$
+.\"
+.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 paste, 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..ca42301
--- /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:")) != -1)
+ 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..dc7f0d3
--- /dev/null
+++ b/usr.bin/m4/pathnames.h
@@ -0,0 +1,60 @@
+/* $OpenBSD: pathnames.h,v 1.4 1997/04/04 18:41:29 deraadt Exp $ */
+/* $NetBSD: pathnames.h,v 1.6 1995/09/29 00:27:55 cgd Exp $ */
+
+/*
+ * 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
+
+#if defined(unix) || defined(__NetBSD__) || defined(__OpenBSD__)
+#define _PATH_DIVNAME "/tmp/m4.0XXXXXXXXXX" /* 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..8724206
--- /dev/null
+++ b/usr.bin/mail/Makefile
@@ -0,0 +1,18 @@
+# @(#)Makefile 8.2 (Berkeley) 1/25/94
+
+PROG= mail
+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..50e7883
--- /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 /var/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=/var/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..87f0acf
--- /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 /var/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..b62f8c5
--- /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 /var/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..b09afbd
--- /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
+/var/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..f6f7211
--- /dev/null
+++ b/usr.bin/mail/aux.c
@@ -0,0 +1,707 @@
+/*
+ * 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. */
+};
+#define SSTACK_SIZE 64 /* XXX was NOFILE. */
+static struct sstack sstack[SSTACK_SIZE];
+
+/*
+ * 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 >= SSTACK_SIZE - 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..ed46fb3
--- /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_messages(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..47fb3c6
--- /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_messages, 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..6ed65a4
--- /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 <termios.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..508e281
--- /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_messages __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..45d5cdc
--- /dev/null
+++ b/usr.bin/mail/mail.1
@@ -0,0 +1,1045 @@
+.\" 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
+.\" $Id$
+.\"
+.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 the system-wide
+.Pa mail.rc
+files 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/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 system wide 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 more
+.Pq Ic \mo
+Takes a list of messages and invokes the pager on that list.
+.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 (preferably in one of the system-wide
+.Pa mail.rc
+files).
+.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. Also, if the
+.Ev MAIL
+environment variable is set, it is used as the
+location of the user's mailbox instead of the
+default in /var/mail.
+.Sh FILES
+.Bl -tag -width /usr/share/misc/mail.*help -compact
+.It Pa /var/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.
+.sp
+.It Pa /usr/share/misc/mail.rc
+.It Pa /usr/local/etc/mail.rc
+.It Pa /etc/mail.rc
+System-wide initialization files. Each file will be sourced, in order,
+if it exists.
+.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
+command
+appeared in
+.At v1 .
+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..d6499bb
--- /dev/null
+++ b/usr.bin/mail/main.c
@@ -0,0 +1,311 @@
+/*
+ * 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 <err.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")) != -1) {
+ 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) {
+ char *s, *path_rc;
+
+ path_rc = malloc(sizeof(_PATH_MASTER_RC));
+ if (path_rc == NULL)
+ errx(1, "malloc(path_rc) failed");
+
+ strcpy(path_rc, _PATH_MASTER_RC);
+ while ((s = strsep(&path_rc, ":")) != NULL)
+ if (*s != '\0')
+ load(s);
+ }
+
+ /*
+ * 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 winsize ws;
+ struct termios tio;
+ speed_t speed = 0;
+
+ if (ioctl(1, TIOCGWINSZ, (char *) &ws) < 0)
+ ws.ws_col = ws.ws_row = 0;
+ if (tcgetattr(1, &tio) != -1)
+ speed = cfgetospeed(&tio);
+ if (speed <= 0)
+ speed = B9600;
+ if (speed < B1200)
+ screenheight = 9;
+ else if (speed == 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..f5c5dd2
--- /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 /var/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..0b2586f
--- /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 "/usr/share/misc/mail.rc:/usr/local/etc/mail.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..53cb456
--- /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 termios tio;
+ 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 (tcgetattr(fileno(stdin), &tio) < 0) {
+ perror("tcgetattr(stdin)");
+ return(-1);
+ }
+ c_erase = tio.c_cc[VERASE];
+ c_kill = tio.c_cc[VKILL];
+#ifndef TIOCSTI
+ tio.c_cc[VERASE] = 0;
+ tio.c_cc[VKILL] = 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++, tcsetattr(fileno(stdin), TCSADRAIN, &tio);
+#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++, tcsetattr(fileno(stdin), TCSADRAIN, &tio);
+#endif
+ hp->h_subject = readtty("Subject: ", hp->h_subject);
+ }
+ if (gflags & GCC) {
+#ifndef TIOCSTI
+ if (!ttyset && hp->h_cc != NIL)
+ ttyset++, tcsetattr(fileno(stdin), TCSADRAIN, &tio);
+#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++, tcsetattr(fileno(stdin), TCSADRAIN, &tio);
+#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
+ tio.c_cc[VERASE] = c_erase;
+ tio.c_cc[VKILL] = c_kill;
+ if (ttyset)
+ tcsetattr(fileno(stdin), TCSADRAIN, &tio);
+ 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..ade3cbd
--- /dev/null
+++ b/usr.bin/mail/v7.local.c
@@ -0,0 +1,88 @@
+/*
+ * 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;
+{
+ char *tmp = getenv("MAIL");
+
+ if (tmp == NULL)
+ (void)sprintf(buf, "%s/%s", _PATH_MAILDIR, user);
+ else
+ (void)strcpy(buf, tmp);
+}
+
+/*
+ * 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..7a9b432
--- /dev/null
+++ b/usr.bin/make/Makefile
@@ -0,0 +1,15 @@
+# @(#)Makefile 5.2 (Berkeley) 12/28/90
+# $Id$
+
+PROG= make
+CFLAGS+= -I${.CURDIR}
+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 util.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..432ff31
--- /dev/null
+++ b/usr.bin/make/PSD.doc/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 8/14/93
+# $Id$
+
+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..7798011
--- /dev/null
+++ b/usr.bin/make/PSD.doc/tutorial.ms
@@ -0,0 +1,3744 @@
+.\" 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
+.\" $Id$
+.\"
+.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.
+.\"
+.\" @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\-m\fP \fIdirectory\fP"
+.Ix 0 def flags -m
+Tells PMake another place to search for included makefiles via the <...>
+style. Several
+.B \-m
+options can be given to form a search path. If this construct is used the
+default system makefile search path is completely overridden.
+To be explained in chapter 3, section 3.2.
+.Rm 2 3.2
+.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 (or directories)
+(to find out what that directory is, give PMake the
+.B \-h
+flag).
+.Ix 0 ref flags -h
+The system makefile directory search path can be overridden via the
+.B \-m
+option.
+.Ix 0 ref flags -m
+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..769e6ae
--- /dev/null
+++ b/usr.bin/make/arch.c
@@ -0,0 +1,1234 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 <utime.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 */
+ char *fnametab; /* Extended name table strings */
+ size_t fnamesize; /* Size of the string table */
+} 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 *));
+#if defined(__svr4__) || defined(__SVR4)
+#define SVR4ARCHIVES
+static int ArchSVR4Entry __P((Arch *, char *, size_t, FILE *));
+#endif
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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);
+ if (a->fnametab)
+ free(a->fnametab);
+ 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)) != NULL)
+ 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 = estrdup (archive);
+ ar->fnametab = NULL;
+ ar->fnamesize = 0;
+ 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...
+ */
+ goto badarch;
+ } else {
+ /*
+ * 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);
+
+ (void) strncpy (memName, arh.ar_name, sizeof(arh.ar_name));
+ for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) {
+ continue;
+ }
+ cp[1] = '\0';
+
+#ifdef SVR4ARCHIVES
+ /*
+ * svr4 names are slash terminated. Also svr4 extended AR format.
+ */
+ if (memName[0] == '/') {
+ /*
+ * svr4 magic mode; handle it
+ */
+ switch (ArchSVR4Entry(ar, memName, size, arch)) {
+ case -1: /* Invalid data */
+ goto badarch;
+ case 0: /* List of files entry */
+ continue;
+ default: /* Got the entry */
+ break;
+ }
+ }
+ else {
+ if (cp[0] == '/')
+ cp[0] = '\0';
+ }
+#endif
+
+#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)
+ goto badarch;
+ if (fread (memName, elen, 1, arch) != 1)
+ goto badarch;
+ 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));
+ }
+ 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);
+ }
+
+badarch:
+ fclose (arch);
+ Hash_DeleteTable (&ar->members);
+ if (ar->fnametab)
+ free(ar->fnametab);
+ free ((Address)ar);
+ return ((struct ar_hdr *) NULL);
+}
+
+#ifdef SVR4ARCHIVES
+/*-
+ *-----------------------------------------------------------------------
+ * ArchSVR4Entry --
+ * Parse an SVR4 style entry that begins with a slash.
+ * If it is "//", then load the table of filenames
+ * If it is "/<offset>", then try to substitute the long file name
+ * from offset of a table previously read.
+ *
+ * Results:
+ * -1: Bad data in archive
+ * 0: A table was loaded from the file
+ * 1: Name was successfully substituted from table
+ * 2: Name was not successfully substituted from table
+ *
+ * Side Effects:
+ * If a table is read, the file pointer is moved to the next archive
+ * member
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+ArchSVR4Entry(ar, name, size, arch)
+ Arch *ar;
+ char *name;
+ size_t size;
+ FILE *arch;
+{
+#define ARLONGNAMES1 "//"
+#define ARLONGNAMES2 "/ARFILENAMES"
+ size_t entry;
+ char *ptr, *eptr;
+
+ if (strncmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 ||
+ strncmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) {
+
+ if (ar->fnametab != NULL) {
+ if (DEBUG(ARCH)) {
+ printf("Attempted to redefine an SVR4 name table\n");
+ }
+ return -1;
+ }
+
+ /*
+ * This is a table of archive names, so we build one for
+ * ourselves
+ */
+ ar->fnametab = emalloc(size);
+ ar->fnamesize = size;
+
+ if (fread(ar->fnametab, size, 1, arch) != 1) {
+ if (DEBUG(ARCH)) {
+ printf("Reading an SVR4 name table failed\n");
+ }
+ return -1;
+ }
+ eptr = ar->fnametab + size;
+ for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++)
+ switch (*ptr) {
+ case '/':
+ entry++;
+ *ptr = '\0';
+ break;
+
+ case '\n':
+ break;
+
+ default:
+ break;
+ }
+ if (DEBUG(ARCH)) {
+ printf("Found svr4 archive name table with %d entries\n", entry);
+ }
+ return 0;
+ }
+
+ if (name[1] == ' ' || name[1] == '\0')
+ return 2;
+
+ entry = (size_t) strtol(&name[1], &eptr, 0);
+ if ((*eptr != ' ' && *eptr != '\0') || eptr == &name[1]) {
+ if (DEBUG(ARCH)) {
+ printf("Could not parse SVR4 name %s\n", name);
+ }
+ return 2;
+ }
+ if (entry >= ar->fnamesize) {
+ if (DEBUG(ARCH)) {
+ printf("SVR4 entry offset %s is greater than %d\n",
+ name, ar->fnamesize);
+ }
+ return 2;
+ }
+
+ if (DEBUG(ARCH)) {
+ printf("Replaced %s with %s\n", name, &ar->fnametab[entry]);
+ }
+
+ (void) strncpy(name, &ar->fnametab[entry], MAXPATHLEN);
+ name[MAXPATHLEN] = '\0';
+ return 1;
+}
+#endif
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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 */
+{
+#ifdef RANLIBMAG
+ FILE * arch; /* Stream open to archive */
+ struct ar_hdr arh; /* Header describing table of contents */
+ struct utimbuf times; /* Times for utime() 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.actime = times.modtime = now;
+ utime(gn->path, &times);
+ }
+#endif
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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 {
+#ifdef RANLIBMAG
+ 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;
+ }
+#else
+ oodate = FALSE;
+#endif
+ }
+ 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/buf.c b/usr.bin/make/buf.c
new file mode 100644
index 0000000..462cbef
--- /dev/null
+++ b/usr.bin/make/buf.c
@@ -0,0 +1,463 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 *) erealloc((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);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Buf_ReplaceLastByte --
+ * Replace the last byte in a buffer.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * If the buffer was empty intially, then a new byte will be added.
+ * Otherwise, the last byte is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Buf_ReplaceLastByte (buf, byte)
+ Buffer buf; /* buffer to augment */
+ Byte byte; /* byte to be written */
+{
+ if (buf->inPtr == buf->outPtr)
+ Buf_AddByte(buf, byte);
+ else
+ *(buf->inPtr - 1) = byte;
+}
diff --git a/usr.bin/make/buf.h b/usr.bin/make/buf.h
new file mode 100644
index 0000000..562739d
--- /dev/null
+++ b/usr.bin/make/buf.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ *
+ * from: @(#)buf.h 8.1 (Berkeley) 6/6/93
+ * $Id$
+ */
+
+/*-
+ * 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));
+void Buf_ReplaceLastByte __P((Buffer, Byte));
+
+#endif /* _BUF_H */
diff --git a/usr.bin/make/compat.c b/usr.bin/make/compat.c
new file mode 100644
index 0000000..a646103
--- /dev/null
+++ b/usr.bin/make/compat.c
@@ -0,0 +1,654 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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/stat.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.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);
+
+ if (!noExecute && eunlink(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 */
+ int 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(&reason)) != cpid) {
+ if (stat == -1 && errno != EINTR) {
+ break;
+ }
+ }
+
+ if (stat > -1) {
+ if (WIFSTOPPED(reason)) {
+ status = WSTOPSIG(reason); /* stopped */
+ } else if (WIFEXITED(reason)) {
+ status = WEXITSTATUS(reason); /* exited */
+ if (status != 0) {
+ printf ("*** Error code %d", status);
+ }
+ } else {
+ status = WTERMSIG(reason); /* 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);
+ if (gn->made == ERROR) {
+ printf("\n\nStop.\n");
+ exit(1);
+ }
+ }
+ }
+
+ /*
+ * 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..d5c1fc2
--- /dev/null
+++ b/usr.bin/make/cond.c
@@ -0,0 +1,1256 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 void CondPushBack __P((Token));
+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) __P((int, char *)); /* 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 },
+ { NULL, 0, FALSE, NULL }
+};
+
+static Boolean condInvert; /* Invert the default function */
+static Boolean (*condDefProc) /* Default function to apply */
+ __P((int, char *));
+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) __P((int, char *));
+ 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..d72e654
--- /dev/null
+++ b/usr.bin/make/config.h
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ *
+ * from: @(#)config.h 8.1 (Berkeley) 6/6/93
+ * $Id$
+ */
+
+#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.
+ */
+#define LIBSUFF ".a"
+#define RECHECK
+
+/*
+ * POSIX
+ * Adhere to the POSIX 1003.2 draft for the make(1) program.
+ * - Use MAKEFLAGS instead of MAKE to pick arguments from the
+ * environment.
+ * - Allow empty command lines if starting with tab.
+ */
+#define POSIX
+
+/*
+ * SYSVINCLUDE
+ * Recognize system V like include directives [include "filename"]
+ * SYSVVARSUB
+ * Recognize system V like ${VAR:x=y} variable substitutions
+ */
+#define SYSVINCLUDE
+#define SYSVVARSUB
+
+/*
+ * SUNSHCMD
+ * Recognize SunOS and Solaris:
+ * VAR :sh= CMD # Assign VAR to the command substitution of CMD
+ * ${VAR:sh} # Return the command substitution of the value
+ * # of ${VAR}
+ */
+#define SUNSHCMD
+
+#if !defined(__svr4__) && !defined(__SVR4)
+# ifndef RANLIBMAG
+# define RANLIBMAG "__.SYMDEF"
+# endif
+#endif
diff --git a/usr.bin/make/dir.c b/usr.bin/make/dir.c
new file mode 100644
index 0000000..9fcf94f
--- /dev/null
+++ b/usr.bin/make/dir.c
@@ -0,0 +1,1276 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 ? estrdup(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 (estrdup (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 = estrdup(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 (estrdup (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(estrdup(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 (estrdup (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 = estrdup(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 = estrdup (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) {
+#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */
+ /*
+ * 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 && d_ino */
+ (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 = estrdup ("");
+
+ 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..62687c5
--- /dev/null
+++ b/usr.bin/make/dir.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ *
+ * from: @(#)dir.h 8.1 (Berkeley) 6/6/93
+ * $Id$
+ */
+
+/* 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..3d8617f
--- /dev/null
+++ b/usr.bin/make/for.c
@@ -0,0 +1,301 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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_AtFront(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..a8250a4
--- /dev/null
+++ b/usr.bin/make/hash.c
@@ -0,0 +1,420 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..6e88ed8
--- /dev/null
+++ b/usr.bin/make/hash.h
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ *
+ * from: @(#)hash.h 8.1 (Berkeley) 6/6/93
+ * $Id$
+ */
+
+/* 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..01e493f
--- /dev/null
+++ b/usr.bin/make/job.c
@@ -0,0 +1,3107 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <utime.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"
+#ifdef REMOTE
+#include "rmt.h"
+# define STATIC
+#else
+# define STATIC static
+#endif
+
+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 */
+
+/*
+ * XXX: Avoid SunOS bug... FILENO() is fp->_file, and file
+ * is a char! So when we go above 127 we turn negative!
+ */
+#define FILENO(a) ((unsigned) fileno(a))
+
+/*
+ * 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,
+ TRUE, "set -e", "set +e",
+#ifdef OLDBOURNESHELL
+ FALSE, "echo \"%s\"\n", "sh -c '%s || exit 0'\n",
+#endif
+ "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 = 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 */
+STATIC int nJobs; /* The number of children currently running */
+STATIC int nLocal; /* The number of local children */
+STATIC Lst jobs; /* The structures that describe them */
+STATIC 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
+
+STATIC GNode *lastNode; /* The node for which output was most recently
+ * produced. */
+STATIC char *targFmt; /* Format string to use to head output from a
+ * job when it's not the most-recent job heard
+ * from */
+
+#ifdef REMOTE
+# define TARG_FMT "--- %s at %s ---\n" /* Default format */
+# define MESSAGE(fp, gn) \
+ (void) fprintf(fp, targFmt, gn->name, gn->rem.hname);
+#else
+# define TARG_FMT "--- %s ---\n" /* Default format */
+# define MESSAGE(fp, gn) \
+ (void) fprintf(fp, targFmt, gn->name);
+#endif
+
+/*
+ * 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.
+ */
+STATIC 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
+
+/*
+ * Grmpf... There is no way to set bits of the wait structure
+ * anymore with the stupid W*() macros. I liked the union wait
+ * stuff much more. So, we devise our own macros... This is
+ * really ugly, use dramamine sparingly. You have been warned.
+ */
+#define W_SETMASKED(st, val, fun) \
+ { \
+ int sh = (int) ~0; \
+ int mask = fun(sh); \
+ \
+ for (sh = 0; ((mask >> sh) & 1) == 0; sh++) \
+ continue; \
+ *(st) = (*(st) & ~mask) | ((val) << sh); \
+ }
+
+#define W_SETTERMSIG(st, val) W_SETMASKED(st, val, WTERMSIG)
+#define W_SETEXITSTATUS(st, val) W_SETMASKED(st, val, WEXITSTATUS)
+
+
+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 JobClose __P((Job *));
+#ifdef REMOTE
+static int JobCmpRmtID __P((Job *, int));
+# ifdef RMT_WILL_WATCH
+static void JobLocalInput __P((int, Job *));
+# endif
+#else
+static void JobFinish __P((Job *, int *));
+static void JobExec __P((Job *, char **));
+#endif
+static void JobMakeArgv __P((Job *, char **));
+static void JobRestart __P((Job *));
+static int JobStart __P((GNode *, int, Job *));
+static char *JobOutput __P((Job *, char *, char *, int));
+static void JobDoOutput __P((Job *, Boolean));
+static Shell *JobMatchShell __P((char *));
+static void JobInterrupt __P((int, int));
+static void JobRestartJobs __P((void));
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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.
+ */
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout,
+ "JobCondPassSig passing signal %d to child %d.\n",
+ signo, job->pid);
+ (void) fflush(stdout);
+ }
+ 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 */
+{
+ sigset_t nmask, omask;
+ struct sigaction act;
+
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "JobPassSig(%d) called.\n", signo);
+ (void) fflush(stdout);
+ }
+ Lst_ForEach(jobs, JobCondPassSig, (ClientData) &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, signo);
+ } else if ((signo == SIGHUP) || (signo == SIGTERM) || (signo == SIGQUIT)) {
+ JobInterrupt(FALSE, signo);
+ }
+
+ /*
+ * 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.
+ */
+ sigemptyset(&nmask);
+ sigaddset(&nmask, signo);
+ sigprocmask(SIG_SETMASK, &nmask, &omask);
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(signo, &act, NULL);
+
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout,
+ "JobPassSig passing signal to self, mask = %x.\n",
+ ~0 & ~(1 << (signo-1)));
+ (void) fflush(stdout);
+ }
+ (void) signal(signo, SIG_DFL);
+
+ (void) KILL(getpid(), signo);
+
+ signo = SIGCONT;
+ Lst_ForEach(jobs, JobCondPassSig, (ClientData) &signo);
+
+ (void) sigprocmask(SIG_SETMASK, &omask, NULL);
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ act.sa_handler = JobPassSig;
+ sigaction(signo, &act, NULL);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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;
+}
+
+#ifdef REMOTE
+/*-
+ *-----------------------------------------------------------------------
+ * JobCmpRmtID --
+ * Compare the rmtID of the job with the given rmtID and return 0 if they
+ * are equal.
+ *
+ * Results:
+ * 0 if the rmtID's match
+ *
+ * Side Effects:
+ * None.
+ *-----------------------------------------------------------------------
+ */
+static int
+JobCmpRmtID(job, rmtID)
+ ClientData job; /* job to examine */
+ ClientData rmtID; /* remote id desired */
+{
+ return(*(int *) rmtID - *(int *) job->rmtID);
+}
+#endif
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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)) { \
+ (void) fprintf(stdout, fmt, arg); \
+ (void) fflush(stdout); \
+ } \
+ (void) fprintf(job->cmdFILE, fmt, arg); \
+ (void) fflush(job->cmdFILE);
+
+ 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);
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobClose --
+ * Called to close both input and output pipes when a job is finished.
+ *
+ * Results:
+ * Nada
+ *
+ * Side Effects:
+ * The file descriptors associated with the job are closed.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+JobClose(job)
+ Job *job;
+{
+ 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);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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 */
+ int *status; /* sub-why job went away */
+{
+ Boolean done;
+
+ if ((WIFEXITED(*status) &&
+ (((WEXITSTATUS(*status) != 0) && !(job->flags & JOB_IGNERR)))) ||
+ (WIFSIGNALED(*status) && (WTERMSIG(*status) != 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...
+ */
+#ifdef REMOTE
+ KILL(job->pid, SIGCONT);
+#endif
+ JobClose(job);
+ if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
+ (void) fclose(job->cmdFILE);
+ }
+ done = TRUE;
+#ifdef REMOTE
+ if (job->flags & JOB_REMOTE)
+ Rmt_Done(job->rmtID, job->node);
+#endif
+ } else if (WIFEXITED(*status)) {
+ /*
+ * 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.
+ */
+ done = WEXITSTATUS(*status) != 0;
+ /*
+ * Old comment said: "Note we don't
+ * want to close down any of the streams until we know we're at the
+ * end."
+ * But we do. Otherwise when are we going to print the rest of the
+ * stuff?
+ */
+ JobClose(job);
+#ifdef REMOTE
+ if (job->flags & JOB_REMOTE)
+ Rmt_Done(job->rmtID, job->node);
+#endif /* REMOTE */
+ } else {
+ /*
+ * No need to close things down or anything.
+ */
+ done = FALSE;
+ }
+
+ if (done ||
+ WIFSTOPPED(*status) ||
+ (WIFSIGNALED(*status) && (WTERMSIG(*status) == SIGCONT)) ||
+ DEBUG(JOB))
+ {
+ FILE *out;
+
+ if (compatMake && !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 (DEBUG(JOB)) {
+ (void) fprintf(stdout, "Process %d exited.\n", job->pid);
+ (void) fflush(stdout);
+ }
+ if (WEXITSTATUS(*status) != 0) {
+ if (usePipes && job->node != lastNode) {
+ MESSAGE(out, job->node);
+ lastNode = job->node;
+ }
+ (void) fprintf(out, "*** Error code %d%s\n",
+ WEXITSTATUS(*status),
+ (job->flags & JOB_IGNERR) ? "(ignored)" : "");
+
+ if (job->flags & JOB_IGNERR) {
+ *status = 0;
+ }
+ } else if (DEBUG(JOB)) {
+ if (usePipes && job->node != lastNode) {
+ MESSAGE(out, job->node);
+ lastNode = job->node;
+ }
+ (void) fprintf(out, "*** Completed successfully\n");
+ }
+ } else if (WIFSTOPPED(*status)) {
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "Process %d stopped.\n", job->pid);
+ (void) fflush(stdout);
+ }
+ if (usePipes && job->node != lastNode) {
+ MESSAGE(out, job->node);
+ lastNode = job->node;
+ }
+ if (!(job->flags & JOB_REMIGRATE)) {
+ (void) fprintf(out, "*** Stopped -- signal %d\n",
+ WSTOPSIG(*status));
+ }
+ job->flags |= JOB_RESUME;
+ (void)Lst_AtEnd(stoppedJobs, (ClientData)job);
+#ifdef REMOTE
+ if (job->flags & JOB_REMIGRATE)
+ JobRestart(job);
+#endif
+ (void) fflush(out);
+ return;
+ } else if (WTERMSIG(*status) == 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) {
+ MESSAGE(out, job->node);
+ lastNode = job->node;
+ }
+ (void) fprintf(out, "*** Continued\n");
+ }
+ if (!(job->flags & JOB_CONTINUING)) {
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout,
+ "Warning: process %d was not continuing.\n",
+ job->pid);
+ (void) fflush(stdout);
+ }
+#ifdef notdef
+ /*
+ * We don't really want to restart a job from scratch just
+ * because it continued, especially not without killing the
+ * continuing process! That's why this is ifdef'ed out.
+ * FD - 9/17/90
+ */
+ JobRestart(job);
+#endif
+ }
+ job->flags &= ~JOB_CONTINUING;
+ Lst_AtEnd(jobs, (ClientData)job);
+ nJobs += 1;
+ if (!(job->flags & JOB_REMOTE)) {
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout,
+ "Process %d is continuing locally.\n",
+ job->pid);
+ (void) fflush(stdout);
+ }
+ nLocal += 1;
+ }
+ if (nJobs == maxJobs) {
+ jobFull = TRUE;
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "Job queue is full.\n");
+ (void) fflush(stdout);
+ }
+ }
+ (void) fflush(out);
+ return;
+ } else {
+ if (usePipes && job->node != lastNode) {
+ MESSAGE(out, job->node);
+ lastNode = job->node;
+ }
+ (void) fprintf(out, "*** Signal %d\n", WTERMSIG(*status));
+ }
+
+ (void) 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 (compatMake && (WIFEXITED(*status) &&
+ !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;
+ W_SETEXITSTATUS(status, 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 == 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 != 0) {
+ errors += 1;
+ free((Address)job);
+ }
+
+ JobRestartJobs();
+
+ /*
+ * 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) eunlink(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 utimbuf times; /* Times for utime() 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) {
+ (void) fprintf(stdout, "touch %s\n", gn->name);
+ (void) fflush(stdout);
+ }
+
+ 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.actime = times.modtime = now;
+ if (utime(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) {
+ (void) lseek(streamID, 0L, L_SET);
+ (void) write(streamID, &c, 1);
+ }
+
+ (void) close(streamID);
+ } else {
+ (void) fprintf(stdout, "*** couldn't touch %s: %s",
+ file, strerror(errno));
+ (void) fflush(stdout);
+ }
+ }
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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.
+ */
+ static const char msg[] = "make: don't know how to make";
+
+ if (gn->type & OP_OPTIONAL) {
+ (void) fprintf(stdout, "%s %s(ignored)\n", msg, gn->name);
+ (void) fflush(stdout);
+ } else if (keepgoing) {
+ (void) fprintf(stdout, "%s %s(continuing)\n", msg, gn->name);
+ (void) fflush(stdout);
+ return FALSE;
+ } else {
+ (*abortProc)("%s %s. Stop", msg, 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;
+
+ (void) fprintf(stdout, "Running %s %sly\n", job->node->name,
+ job->flags&JOB_REMOTE?"remote":"local");
+ (void) fprintf(stdout, "\tCommand: ");
+ for (i = 0; argv[i] != NULL; i++) {
+ (void) fprintf(stdout, "%s ", argv[i]);
+ }
+ (void) fprintf(stdout, "\n");
+ (void) fflush(stdout);
+ }
+
+ /*
+ * 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)) {
+ MESSAGE(stdout, job->node);
+ 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.
+ */
+ if (dup2(FILENO(job->cmdFILE), 0) == -1)
+ Punt("Cannot dup2: %s", strerror(errno));
+ (void) fcntl(0, F_SETFD, 0);
+ (void) lseek(0, 0, L_SET);
+
+ if (usePipes) {
+ /*
+ * Set up the child's output to be routed through the pipe
+ * we've created for it.
+ */
+ if (dup2(job->outPipe, 1) == -1)
+ Punt("Cannot dup2: %s", strerror(errno));
+ } else {
+ /*
+ * We're capturing output in a file, so we duplicate the
+ * descriptor to the temporary file into the standard
+ * output.
+ */
+ if (dup2(job->outFd, 1) == -1)
+ Punt("Cannot dup2: %s", strerror(errno));
+ }
+ /*
+ * 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.
+ */
+ (void) fcntl(1, F_SETFD, 0);
+ if (dup2(1, 2) == -1)
+ Punt("Cannot dup2: %s", strerror(errno));
+
+#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.
+ */
+# if defined(SYSV)
+ (void) setsid();
+# else
+ (void) setpgid(0, getpid());
+# endif
+#endif /* USE_PGRP */
+
+#ifdef REMOTE
+ if (job->flags & JOB_REMOTE) {
+ Rmt_Exec(shellPath, argv, FALSE);
+ } else
+#endif /* REMOTE */
+ (void) execv(shellPath, argv);
+
+ (void) write(2, "Could not execute shell\n",
+ sizeof("Could not execute shell"));
+ _exit(1);
+ } else {
+#ifdef REMOTE
+ long omask = sigblock(sigmask(SIGCHLD));
+#endif
+ 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) {
+#ifndef REMOTE
+ job->rmtID = 0;
+#else
+ job->rmtID = Rmt_LastID(job->pid);
+#endif /* REMOTE */
+ } else {
+ nLocal += 1;
+ /*
+ * XXX: Used to not happen if REMOTE. Why?
+ */
+ if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
+ (void) fclose(job->cmdFILE);
+ job->cmdFILE = NULL;
+ }
+ }
+#ifdef REMOTE
+ (void) sigsetmask(omask);
+#endif
+ }
+
+#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] = 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 */
+{
+#ifdef REMOTE
+ int host;
+#endif
+
+ if (job->flags & JOB_REMIGRATE) {
+ if (
+#ifdef REMOTE
+ verboseRemigrates ||
+#endif
+ DEBUG(JOB)) {
+ (void) fprintf(stdout, "*** remigrating %x(%s)\n",
+ job->pid, job->node->name);
+ (void) fflush(stdout);
+ }
+
+#ifdef REMOTE
+ if (!Rmt_ReExport(job->pid, job->node, &host)) {
+ if (verboseRemigrates || DEBUG(JOB)) {
+ (void) fprintf(stdout, "*** couldn't migrate...\n");
+ (void) fflush(stdout);
+ }
+#endif
+ 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 (
+#ifdef REMOTE
+ verboseRemigrates ||
+#endif
+ DEBUG(JOB)) {
+ (void) fprintf(stdout, "*** resuming on local machine\n");
+ (void) fflush(stdout);
+ }
+ KILL(job->pid, SIGCONT);
+ nLocal +=1;
+#ifdef REMOTE
+ job->flags &= ~(JOB_REMIGRATE|JOB_RESUME|JOB_REMOTE);
+ job->flags |= JOB_CONTINUING;
+#else
+ job->flags &= ~(JOB_REMIGRATE|JOB_RESUME);
+#endif
+ } else {
+ /*
+ * Job cannot be restarted. Mark the table as full and
+ * place the job back on the list of stopped jobs.
+ */
+ if (
+#ifdef REMOTE
+ verboseRemigrates ||
+#endif
+ DEBUG(JOB)) {
+ (void) fprintf(stdout, "*** holding\n");
+ (void) fflush(stdout);
+ }
+ (void)Lst_AtFront(stoppedJobs, (ClientData)job);
+ jobFull = TRUE;
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "Job queue is full.\n");
+ (void) fflush(stdout);
+ }
+ return;
+ }
+#ifdef REMOTE
+ } else {
+ /*
+ * Clear out the remigrate and resume flags. Set the continuing
+ * flag so we know later on that the process isn't exiting just
+ * because of a signal.
+ */
+ job->flags &= ~(JOB_REMIGRATE|JOB_RESUME);
+ job->flags |= JOB_CONTINUING;
+ job->rmtID = host;
+ }
+#endif
+
+ (void)Lst_AtEnd(jobs, (ClientData)job);
+ nJobs += 1;
+ if (nJobs == maxJobs) {
+ jobFull = TRUE;
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "Job queue is full.\n");
+ (void) fflush(stdout);
+ }
+ }
+ } 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)) {
+ (void) fprintf(stdout, "Restarting %s...", job->node->name);
+ (void) fflush(stdout);
+ }
+#ifdef REMOTE
+ if ((job->node->type&OP_NOEXPORT) ||
+ (nLocal < maxLocal && runLocalFirst)
+# ifdef RMT_NO_EXEC
+ || !Rmt_Export(shellPath, argv, job)
+# else
+ || !Rmt_Begin(shellPath, argv, job->node)
+# endif
+#endif
+ {
+ 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)) {
+ (void) fprintf(stdout, "holding\n");
+ (void) fflush(stdout);
+ }
+ (void)Lst_AtFront(stoppedJobs, (ClientData)job);
+ jobFull = TRUE;
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "Job queue is full.\n");
+ (void) fflush(stdout);
+ }
+ return;
+ } else {
+ /*
+ * Job may be run locally.
+ */
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "running locally\n");
+ (void) fflush(stdout);
+ }
+ job->flags &= ~JOB_REMOTE;
+ }
+ }
+#ifdef REMOTE
+ else {
+ /*
+ * Can be exported. Hooray!
+ */
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "exporting\n");
+ (void) fflush(stdout);
+ }
+ job->flags |= JOB_REMOTE;
+ }
+#endif
+ JobExec(job, argv);
+ } else {
+ /*
+ * The job has stopped and needs to be restarted. Why it stopped,
+ * we don't know...
+ */
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "Resuming %s...", job->node->name);
+ (void) fflush(stdout);
+ }
+ if (((job->flags & JOB_REMOTE) ||
+ (nLocal < maxLocal) ||
+#ifdef REMOTE
+ (((job->flags & JOB_SPECIAL) &&
+ (job->node->type & OP_NOEXPORT)) &&
+ (maxLocal == 0))) &&
+#else
+ ((job->flags & JOB_SPECIAL) &&
+ (maxLocal == 0))) &&
+#endif
+ (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;
+ int 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;
+ W_SETTERMSIG(&status, SIGCONT);
+ JobFinish(job, &status);
+
+ job->flags &= ~(JOB_RESUME|JOB_CONTINUING);
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "done\n");
+ (void) fflush(stdout);
+ }
+ } else {
+ Error("couldn't resume %s: %s",
+ job->node->name, strerror(errno));
+ status = 0;
+ W_SETEXITSTATUS(&status, 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)) {
+ (void) fprintf(stdout, "table full\n");
+ (void) fflush(stdout);
+ }
+ (void) Lst_AtFront(stoppedJobs, (ClientData)job);
+ jobFull = TRUE;
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "Job queue is full.\n");
+ (void) fflush(stdout);
+ }
+ }
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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 != NULL) {
+ previous->flags &= ~(JOB_FIRST|JOB_IGNERR|JOB_SILENT|JOB_REMOTE);
+ job = previous;
+ } else {
+ job = (Job *) emalloc(sizeof(Job));
+ if (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 (!compatMake && 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 == NULL) {
+ Punt("Could not open %s", tfile);
+ }
+ (void) 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((ClientData) Lst_Datum(ln),
+ (ClientData) 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...
+ */
+ JobClose(job);
+ }
+ }
+ } 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) {
+ MESSAGE(stdout, gn);
+ 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) eunlink(tfile);
+ if (job->cmdFILE != NULL)
+ (void) fclose(job->cmdFILE);
+ } else {
+ (void) 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 {
+ (void) fflush(job->cmdFILE);
+ (void) eunlink(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 (!compatMake || (job->flags & JOB_FIRST)) {
+ if (usePipes) {
+ int fd[2];
+ if (pipe(fd) == -1)
+ Punt("Cannot create pipe: %s", strerror(errno));
+ job->inPipe = fd[0];
+ job->outPipe = fd[1];
+ (void) fcntl(job->inPipe, F_SETFD, 1);
+ (void) fcntl(job->outPipe, F_SETFD, 1);
+ } else {
+ (void) fprintf(stdout, "Remaking `%s'\n", gn->name);
+ (void) 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);
+ }
+ }
+
+#ifdef REMOTE
+ if (!(gn->type & OP_NOEXPORT) && !(runLocalFirst && nLocal < maxLocal)) {
+#ifdef RMT_NO_EXEC
+ local = !Rmt_Export(shellPath, argv, job);
+#else
+ local = !Rmt_Begin(shellPath, argv, job->node);
+#endif /* RMT_NO_EXEC */
+ if (!local) {
+ job->flags |= JOB_REMOTE;
+ }
+ } else
+#endif
+ local = TRUE;
+
+ if (local && (((nLocal >= maxLocal) &&
+ !(job->flags & JOB_SPECIAL) &&
+#ifdef REMOTE
+ (!(gn->type & OP_NOEXPORT) || (maxLocal != 0))
+#else
+ (maxLocal != 0)
+#endif
+ )))
+ {
+ /*
+ * 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. In addition, any target marked with .NOEXPORT will
+ * be run locally if maxLocal is 0.
+ */
+ jobFull = TRUE;
+
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "Can only run job locally.\n");
+ (void) fflush(stdout);
+ }
+ 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)) {
+ (void) fprintf(stdout, "Local job queue is full.\n");
+ (void) fflush(stdout);
+ }
+ }
+ JobExec(job, argv);
+ }
+ return(JOB_RUNNING);
+}
+
+static char *
+JobOutput(job, cp, endp, msg)
+ register Job *job;
+ register char *cp, *endp;
+ int msg;
+{
+ register char *ecp;
+
+ if (commandShell->noPrint) {
+ ecp = Str_FindSubstring(cp, commandShell->noPrint);
+ while (ecp != NULL) {
+ if (cp != ecp) {
+ *ecp = '\0';
+ if (msg && job->node != lastNode) {
+ MESSAGE(stdout, job->node);
+ 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.
+ */
+ (void) fprintf(stdout, "%s", cp);
+ (void) fflush(stdout);
+ }
+ 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 {
+ return cp;
+ }
+ }
+ }
+ return cp;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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 */
+ Boolean fbuf; /* true if our buffer filled up */
+ 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:
+ gotNL = FALSE;
+ fbuf = FALSE;
+
+ 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.
+ */
+ fbuf = TRUE;
+ i = job->curPos;
+ }
+ }
+ if (gotNL || fbuf) {
+ /*
+ * 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) {
+ char *cp;
+
+ cp = JobOutput(job, job->outBuf, &job->outBuf[i], FALSE);
+
+ /*
+ * 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) {
+ MESSAGE(stdout, job->node);
+ lastNode = job->node;
+ }
+ (void) fprintf(stdout, "%s%s", cp, gotNL ? "\n" : "");
+ (void) fflush(stdout);
+ }
+ }
+ if (i < max - 1) {
+ /* shift the remaining characters down */
+ (void) 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 != NULL) {
+ (void) fprintf(stdout, "Results of making %s:\n", job->node->name);
+ (void) fflush(stdout);
+ while (fgets(inLine, sizeof(inLine), oFILE) != NULL) {
+ register char *cp, *endp, *oendp;
+
+ cp = inLine;
+ oendp = endp = inLine + strlen(inLine);
+ if (endp[-1] == '\n') {
+ *--endp = '\0';
+ }
+ cp = JobOutput(job, inLine, endp, FALSE);
+
+ /*
+ * 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.
+ */
+ (void) fprintf(stdout, "%s", cp);
+ (void) fflush(stdout);
+ if (endp != oendp) {
+ (void) fprintf(stdout, "\n");
+ (void) fflush(stdout);
+ }
+ }
+ (void) fclose(oFILE);
+ (void) eunlink(job->outFile);
+ }
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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 */
+ int status; /* Exit/termination status */
+
+ /*
+ * Don't even bother if we know there's no one around.
+ */
+ if (nLocal == 0) {
+ return;
+ }
+
+ while ((pid = waitpid((pid_t) -1, &status,
+ (block?0:WNOHANG)|WUNTRACED)) > 0)
+ {
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "Process %d exited or stopped.\n", pid);
+ (void) fflush(stdout);
+ }
+
+
+ jnode = Lst_Find(jobs, (ClientData)&pid, JobCmpPid);
+
+ if (jnode == NILLNODE) {
+ if (WIFSIGNALED(status) && (WTERMSIG(status) == 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)) {
+ (void) fprintf(stdout, "Job queue is no longer full.\n");
+ (void) fflush(stdout);
+ }
+ jobFull = FALSE;
+#ifdef REMOTE
+ if (!(job->flags & JOB_REMOTE)) {
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout,
+ "Job queue has one fewer local process.\n");
+ (void) fflush(stdout);
+ }
+ nLocal -= 1;
+ }
+#else
+ nLocal -= 1;
+#endif
+ }
+
+ 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
+
+ (void) 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, 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 */
+
+ (void) 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
+#ifdef REMOTE
+ || noMessages
+#endif
+ ) {
+ /*
+ * 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 == 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 == NULL) {
+ commandShell->exit = "";
+ }
+ if (commandShell->echo == 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) {
+ (void) signal(SIGINT, JobPassSig);
+ }
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
+ (void) signal(SIGHUP, JobPassSig);
+ }
+ if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) {
+ (void) signal(SIGQUIT, JobPassSig);
+ }
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
+ (void) 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) {
+ (void) signal(SIGTSTP, JobPassSig);
+ }
+ if (signal(SIGTTOU, SIG_IGN) != SIG_IGN) {
+ (void) signal(SIGTTOU, JobPassSig);
+ }
+ if (signal(SIGTTIN, SIG_IGN) != SIG_IGN) {
+ (void) signal(SIGTTIN, JobPassSig);
+ }
+ if (signal(SIGWINCH, SIG_IGN) != SIG_IGN) {
+ (void) 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;
+ JobRestartJobs();
+ 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 = 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 == NULL || strlen(match->name) < strlen(sh->name)) {
+ match = sh;
+ }
+ }
+ return(match == 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 = 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 == 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 == 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 == NULL) {
+ path = shellPath;
+ } else {
+ path += 1;
+ }
+ if (newShell.name != 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 == NULL) {
+ commandShell->errCheck = "";
+ }
+ if (commandShell->ignErr == 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, signo)
+ int runINTERRUPT; /* Non-zero if commands for the .INTERRUPT
+ * target should be executed */
+ int signo; /* signal received */
+{
+ 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 == NULL ?
+ job->node->name :
+ job->node->path);
+ if (!noExecute && eunlink(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, signo)) {
+ /*
+ * If couldn't kill the thing, finish it out now with an
+ * error code, since no exit report will come in likely.
+ */
+ int status;
+
+ status.w_status = 0;
+ status.w_retcode = 1;
+ JobFinish(job, &status);
+ }
+ } else if (job->pid) {
+ KILL(job->pid, signo);
+ }
+#else
+ if (job->pid) {
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout,
+ "JobInterrupt passing signal to child %d.\n",
+ job->pid);
+ (void) fflush(stdout);
+ }
+ KILL(job->pid, signo);
+ }
+#endif /* RMT_WANTS_SIGNALS */
+ }
+
+#ifdef REMOTE
+ (void)Lst_Open(stoppedJobs);
+ while ((ln = Lst_Next(stoppedJobs)) != NILLNODE) {
+ job = (Job *) Lst_Datum(ln);
+
+ if (job->flags & JOB_RESTART) {
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "%s%s",
+ "JobInterrupt skipping job on stopped queue",
+ "-- it was waiting to be restarted.\n");
+ (void) fflush(stdout);
+ }
+ continue;
+ }
+ if (!Targ_Precious(job->node)) {
+ char *file = (job->node->path == NULL ?
+ job->node->name :
+ job->node->path);
+ if (eunlink(file) == 0) {
+ Error("*** %s removed", file);
+ }
+ }
+ /*
+ * Resume the thing so it will take the signal.
+ */
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout,
+ "JobInterrupt passing CONT to stopped child %d.\n",
+ job->pid);
+ (void) fflush(stdout);
+ }
+ KILL(job->pid, SIGCONT);
+#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.
+ */
+ int status;
+ status.w_status = 0;
+ status.w_retcode = 1;
+ JobFinish(job, &status);
+ }
+ } else if (job->pid) {
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout,
+ "JobInterrupt passing interrupt to stopped child %d.\n",
+ job->pid);
+ (void) fflush(stdout);
+ }
+ KILL(job->pid, SIGINT);
+ }
+#endif /* RMT_WANTS_SIGNALS */
+ }
+#endif
+ Lst_Close(stoppedJobs);
+
+ 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) eunlink(tfile);
+ exit(signo);
+}
+
+/*
+ *-----------------------------------------------------------------------
+ * 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, NULL);
+
+ while (nJobs) {
+ Job_CatchOutput();
+#ifndef RMT_WILL_WATCH
+ Job_CatchChildren(!usePipes);
+#endif /* RMT_WILL_WATCH */
+ }
+ }
+ }
+ (void) eunlink(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 (waitpid((pid_t) -1, &foo, WNOHANG) > 0)
+ continue;
+ (void) eunlink(tfile);
+}
+
+#ifdef REMOTE
+/*-
+ *-----------------------------------------------------------------------
+ * JobFlagForMigration --
+ * Handle the eviction of a child. Called from RmtStatusChange.
+ * Flags the child as remigratable and then suspends it.
+ *
+ * Results:
+ * none.
+ *
+ * Side Effects:
+ * The job descriptor is flagged for remigration.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+JobFlagForMigration(hostID)
+ int hostID; /* ID of host we used, for matching children. */
+{
+ register Job *job; /* job descriptor for dead child */
+ LstNode jnode; /* list element for finding job */
+
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout, "JobFlagForMigration(%d) called.\n", hostID);
+ (void) fflush(stdout);
+ }
+ jnode = Lst_Find(jobs, (ClientData)hostID, JobCmpRmtID);
+
+ if (jnode == NILLNODE) {
+ jnode = Lst_Find(stoppedJobs, (ClientData)hostID, JobCmpRmtID);
+ if (jnode == NILLNODE) {
+ if (DEBUG(JOB)) {
+ Error("Evicting host(%d) not in table", hostID);
+ }
+ return;
+ }
+ }
+ job = (Job *) Lst_Datum(jnode);
+
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout,
+ "JobFlagForMigration(%d) found job '%s'.\n", hostID,
+ job->node->name);
+ (void) fflush(stdout);
+ }
+
+ KILL(job->pid, SIGSTOP);
+
+ job->flags |= JOB_REMIGRATE;
+}
+
+#endif
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobRestartJobs --
+ * Tries to restart stopped jobs if there are slots available.
+ * Note that this tries to restart them regardless of pending errors.
+ * It's not good to leave stopped jobs lying around!
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Resumes(and possibly migrates) jobs.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+JobRestartJobs()
+{
+ while (!jobFull && !Lst_IsEmpty(stoppedJobs)) {
+ if (DEBUG(JOB)) {
+ (void) fprintf(stdout,
+ "Job queue is not full. Restarting a stopped job.\n");
+ (void) fflush(stdout);
+ }
+ JobRestart((Job *)Lst_DeQueue(stoppedJobs));
+ }
+}
diff --git a/usr.bin/make/job.h b/usr.bin/make/job.h
new file mode 100644
index 0000000..2d3e961
--- /dev/null
+++ b/usr.bin/make/job.h
@@ -0,0 +1,234 @@
+/*
+ * 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.
+ *
+ * from: @(#)job.h 8.1 (Berkeley) 6/6/93
+ * $Id: job.h,v 1.7 1997/02/22 19:27:12 peter Exp $
+ */
+
+/*-
+ * 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 100000
+
+
+/*-
+ * 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..fef9642
--- /dev/null
+++ b/usr.bin/make/list.h
@@ -0,0 +1,299 @@
+/*
+ * 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.
+ *
+ * from: @(#)list.h 8.1 (Berkeley) 6/6/93
+ * $Id$
+ */
+
+/*
+ * 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..2d1d867
--- /dev/null
+++ b/usr.bin/make/lst.h
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ *
+ * from: @(#)lst.h 8.1 (Berkeley) 6/6/93
+ * $Id$
+ */
+
+/*-
+ * lst.h --
+ * Header for using the list library
+ */
+#ifndef _LST_H_
+#define _LST_H_
+
+#include <sprite.h>
+#include <sys/param.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..e936f73
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstAppend.c
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..c4f7480
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstAtEnd.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.
+ *
+ * $Id$
+ */
+
+#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..720e663
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstAtFront.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.
+ *
+ * $Id$
+ */
+
+#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..f28119f
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstClose.c
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..5cb8a67
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstConcat.c
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..ec7d11c
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstDatum.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.
+ *
+ * $Id$
+ */
+
+#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..6233502
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstDeQueue.c
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..e241452
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstDestroy.c
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..cc5e75e
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstDupl.c
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..d7627f3
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstEnQueue.c
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..fbbae66
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstFind.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.
+ *
+ * $Id$
+ */
+
+#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..6c64cf1
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstFindFrom.c
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..e7a3039
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstFirst.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.
+ *
+ * $Id$
+ */
+
+#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..1688e9d
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstForEach.c
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..7692c57
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstForEachFrom.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.
+ *
+ * $Id$
+ */
+
+#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) __P((ClientData, ClientData));
+ 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..a16fca2
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstInit.c
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..8c4ebcb
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstInsert.c
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..4260940
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstInt.h
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ *
+ * from: @(#)lstInt.h 8.1 (Berkeley) 6/6/93
+ * $Id$
+ */
+
+/*-
+ * lstInt.h --
+ * Internals for the list library
+ */
+#ifndef _LSTINT_H_
+#define _LSTINT_H_
+
+#include "make.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) emalloc (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..2c48019
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstIsAtEnd.c
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..6a9a7bd
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstIsEmpty.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.
+ *
+ * $Id$
+ */
+
+#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..49b2f8e
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstLast.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.
+ *
+ * $Id$
+ */
+
+#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..cc5cb4f
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstMember.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.
+ *
+ * $Id$
+ */
+
+#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..f6e2656
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstNext.c
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..fbb7d0e
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstOpen.c
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..e84d301
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstRemove.c
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..a2dcb45
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstReplace.c
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..b43a00c
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstSucc.c
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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..94851ad
--- /dev/null
+++ b/usr.bin/make/main.c
@@ -0,0 +1,1255 @@
+/*
+ * 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.
+ *
+ * $Id: main.c,v 1.16 1997/02/22 19:27:14 peter Exp $
+ */
+
+#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>
+#ifndef MACHINE
+#include <sys/utsname.h>
+#endif
+#include <sys/wait.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 */
+static Boolean printVars; /* print value of one or more vars */
+static Lst variables; /* list of variables to print */
+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 void MainParseArgs __P((int, char **));
+char * chdir_verify_path __P((char *, char *));
+static int ReadMakefile __P((ClientData, ClientData));
+static void usage __P((void));
+
+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;
+ int forceJobs = 0;
+
+ optind = 1; /* since we're called more than once */
+#ifdef REMOTE
+# define OPTFLAGS "BD:I:L:PSV:d:ef:ij:km:nqrst"
+#else
+# define OPTFLAGS "BD:I:PSV:d:ef:ij:km:nqrst"
+#endif
+rearg: while((c = getopt(argc, argv, OPTFLAGS)) != -1) {
+ 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;
+ case 'V':
+ printVars = TRUE;
+ (void)Lst_AtEnd(variables, (ClientData)optarg);
+ Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
+ break;
+ case 'B':
+ compatMake = TRUE;
+ break;
+#ifdef REMOTE
+ case 'L':
+ maxLocal = atoi(optarg);
+ Var_Append(MAKEFLAGS, "-L", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
+ break;
+#endif
+ case 'P':
+ usePipes = FALSE;
+ Var_Append(MAKEFLAGS, "-P", VAR_GLOBAL);
+ break;
+ case 'S':
+ keepgoing = FALSE;
+ Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL);
+ break;
+ 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':
+ forceJobs = TRUE;
+ maxJobs = atoi(optarg);
+#ifndef REMOTE
+ maxLocal = maxJobs;
+#endif
+ 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 'm':
+ Dir_AddDir(sysIncPath, optarg);
+ Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, optarg, 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();
+ }
+ }
+
+ /*
+ * Be compatible if user did not specify -j and did not explicitly
+ * turned compatibility on
+ */
+ if (!compatMake && !forceJobs)
+ compatMake = TRUE;
+
+ 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)estrdup(*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);
+}
+
+char *
+chdir_verify_path(path, obpath)
+ char *path;
+ char *obpath;
+{
+ struct stat sb;
+
+ if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
+ if (chdir(path)) {
+ (void)fprintf(stderr, "make warning: %s: %s.\n",
+ path, strerror(errno));
+ return 0;
+ }
+ else {
+ if (path[0] != '/') {
+ (void) snprintf(obpath, MAXPATHLEN, "%s/%s",
+ curdir, path);
+ return obpath;
+ }
+ else
+ return path;
+ }
+ }
+
+ return 0;
+}
+
+
+/*-
+ * 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, *pathp, *pwd;
+ char mdpath[MAXPATHLEN + 1];
+ char obpath[MAXPATHLEN + 1];
+ char cdpath[MAXPATHLEN + 1];
+ char *machine = getenv("MACHINE");
+ Lst sysMkPath; /* Path of sys.mk */
+ char *cp = NULL, *start;
+ /* avoid faults on read-only strings */
+ static char syspath[] = _PATH_DEFSYSPATH;
+
+#ifdef RLIMIT_NOFILE
+ /*
+ * get rid of resource limit on file descriptors
+ */
+ {
+ struct rlimit rl;
+ if (getrlimit(RLIMIT_NOFILE, &rl) != -1 &&
+ rl.rlim_cur != rl.rlim_max) {
+ rl.rlim_cur = rl.rlim_max;
+ (void) setrlimit(RLIMIT_NOFILE, &rl);
+ }
+ }
+#endif
+ /*
+ * 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) {
+#ifndef MACHINE
+ struct utsname utsname;
+
+ if (uname(&utsname) == -1) {
+ perror("make: uname");
+ exit(2);
+ }
+ machine = utsname.machine;
+#else
+ machine = MACHINE;
+#endif
+ }
+
+ /*
+ * The object directory location is determined using the
+ * following order of preference:
+ *
+ * 1. MAKEOBJDIRPREFIX`cwd`
+ * 2. MAKEOBJDIR
+ * 3. _PATH_OBJDIR.${MACHINE}
+ * 4. _PATH_OBJDIR
+ * 5. _PATH_OBJDIRPREFIX${MACHINE}
+ *
+ * If all fails, use the current directory to build.
+ *
+ * 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 (!(pathp = getenv("MAKEOBJDIRPREFIX"))) {
+ if (!(path = getenv("MAKEOBJDIR"))) {
+ path = _PATH_OBJDIR;
+ pathp = _PATH_OBJDIRPREFIX;
+ (void) snprintf(mdpath, MAXPATHLEN, "%s.%s",
+ path, machine);
+ if (!(objdir = chdir_verify_path(mdpath, obpath)))
+ if (!(objdir=chdir_verify_path(path, obpath))) {
+ (void) snprintf(mdpath, MAXPATHLEN,
+ "%s%s", pathp, curdir);
+ if (!(objdir=chdir_verify_path(mdpath,
+ obpath)))
+ objdir = curdir;
+ }
+ }
+ else if (!(objdir = chdir_verify_path(path, obpath)))
+ objdir = curdir;
+ }
+ else {
+ (void) snprintf(mdpath, MAXPATHLEN, "%s%s", pathp, curdir);
+ if (!(objdir = chdir_verify_path(mdpath, obpath)))
+ objdir = curdir;
+ }
+
+ setenv("PWD", objdir, 1);
+
+ create = Lst_Init(FALSE);
+ makefiles = Lst_Init(FALSE);
+ printVars = FALSE;
+ variables = 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;
+
+ maxLocal = DEFMAXLOCAL; /* Set default local max concurrency */
+#ifdef REMOTE
+ maxJobs = DEFMAXJOBS; /* Set default max concurrency */
+#else
+ maxJobs = maxLocal;
+#endif
+ compatMake = FALSE; /* No compat mode */
+
+
+ /*
+ * 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);
+
+
+ /*
+ * If no user-supplied system path was given (through the -m option)
+ * add the directories from the DEFSYSPATH (more than one may be given
+ * as dir1:...:dirn) to the system include path.
+ */
+ if (Lst_IsEmpty(sysIncPath)) {
+ 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);
+ }
+ }
+ }
+
+ /*
+ * 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) {
+ LstNode ln;
+
+ sysMkPath = Lst_Init (FALSE);
+ Dir_Expand (_PATH_DEFSYSMK, sysIncPath, sysMkPath);
+ if (Lst_IsEmpty(sysMkPath))
+ Fatal("make: no system rules (%s).", _PATH_DEFSYSMK);
+ ln = Lst_Find(sysMkPath, (ClientData)NULL, ReadMakefile);
+ if (ln != NILLNODE)
+ Fatal("make: cannot open %s.", (char *)Lst_Datum(ln));
+ }
+
+ 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", NULL))
+ (void)ReadMakefile("Makefile", NULL);
+
+ (void)ReadMakefile(".depend", NULL);
+
+ 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);
+
+ /* print the values of any variables requested by the user */
+ if (printVars) {
+ LstNode ln;
+
+ for (ln = Lst_First(variables); ln != NILLNODE;
+ ln = Lst_Succ(ln)) {
+ char *value = Var_Value((char *)Lst_Datum(ln),
+ VAR_GLOBAL, &p1);
+
+ printf("%s\n", value ? value : "");
+ if (p1)
+ free(p1);
+ }
+ }
+
+ /*
+ * 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);
+
+ if (!compatMake && !printVars) {
+ /*
+ * 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 if (!printVars) {
+ /*
+ * 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(variables, 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(p, q)
+ ClientData p, q;
+{
+ char *fname = p; /* makefile to read */
+ extern Lst parseIncPath;
+ 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);
+}
+
+/*-
+ * Cmd_Exec --
+ * Execute the command in cmd, and return the output of that command
+ * in a string.
+ *
+ * Results:
+ * A string containing the output of the command, or the empty string
+ * If err is not NULL, it contains the reason for the command failure
+ *
+ * Side Effects:
+ * The string must be freed by the caller.
+ */
+char *
+Cmd_Exec(cmd, err)
+ char *cmd;
+ char **err;
+{
+ char *args[4]; /* Args for invoking the shell */
+ int fds[2]; /* Pipe streams */
+ int cpid; /* Child PID */
+ int pid; /* PID from wait() */
+ char *res; /* result */
+ int status; /* command exit status */
+ Buffer buf; /* buffer to store the result */
+ char *cp;
+ int cc;
+
+
+ *err = NULL;
+
+ /*
+ * Set up arguments for shell
+ */
+ args[0] = "sh";
+ args[1] = "-c";
+ args[2] = cmd;
+ args[3] = NULL;
+
+ /*
+ * Open a pipe for fetching its output
+ */
+ if (pipe(fds) == -1) {
+ *err = "Couldn't create pipe for \"%s\"";
+ goto bad;
+ }
+
+ /*
+ * Fork
+ */
+ switch (cpid = vfork()) {
+ case 0:
+ /*
+ * Close input side of pipe
+ */
+ (void) 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?
+ */
+ (void) dup2(fds[1], 1);
+ (void) close(fds[1]);
+
+ (void) execv("/bin/sh", args);
+ _exit(1);
+ /*NOTREACHED*/
+
+ case -1:
+ *err = "Couldn't exec \"%s\"";
+ goto bad;
+
+ default:
+ /*
+ * No need for the writing half
+ */
+ (void) 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.
+ */
+ (void) close(fds[0]);
+
+ /*
+ * Wait for the process to exit.
+ */
+ while(((pid = wait(&status)) != cpid) && (pid >= 0))
+ continue;
+
+ if (cc == -1)
+ *err = "Error reading shell's output for \"%s\"";
+
+ res = (char *)Buf_GetAll (buf, &cc);
+ Buf_Destroy (buf, FALSE);
+
+ if (status)
+ *err = "\"%s\" returned non-zero status";
+
+ /*
+ * 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--;
+ }
+ break;
+ }
+ return res;
+bad:
+ res = emalloc(1);
+ *res = '\0';
+ return res;
+}
+
+/*-
+ * 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.
+ */
+void *
+emalloc(len)
+ size_t len;
+{
+ void *p;
+
+ if ((p = malloc(len)) == NULL)
+ enomem();
+ return(p);
+}
+
+/*
+ * estrdup --
+ * strdup, but die on error.
+ */
+char *
+estrdup(str)
+ const char *str;
+{
+ char *p;
+
+ if ((p = strdup(str)) == NULL)
+ enomem();
+ return(p);
+}
+
+/*
+ * erealloc --
+ * realloc, but die on error.
+ */
+void *
+erealloc(ptr, size)
+ void *ptr;
+ size_t size;
+{
+ if ((ptr = realloc(ptr, size)) == NULL)
+ enomem();
+ return(ptr);
+}
+
+/*
+ * enomem --
+ * die when out of memory.
+ */
+void
+enomem()
+{
+ (void)fprintf(stderr, "make: %s.\n", strerror(errno));
+ exit(2);
+}
+
+/*
+ * enunlink --
+ * Remove a file carefully, avoiding directories.
+ */
+int
+eunlink(file)
+ const char *file;
+{
+ struct stat st;
+
+ if (lstat(file, &st) == -1)
+ return -1;
+
+ if (S_ISDIR(st.st_mode)) {
+ errno = EISDIR;
+ return -1;
+ }
+ return unlink(file);
+}
+
+/*
+ * usage --
+ * exit with usage message
+ */
+static void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: make [-Beiknqrst] [-D variable] [-d flags] [-f makefile ]\n\
+ [-I directory] [-j max_jobs] [-m directory] [-V variable]\n\
+ [variable=value] [target ...]\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..8214e9a
--- /dev/null
+++ b/usr.bin/make/make.1
@@ -0,0 +1,966 @@
+.\" 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: @(#)make.1 8.4 (Berkeley) 3/19/94
+.\" $Id$
+.\"
+.Dd March 19, 1994
+.Dt MAKE 1
+.Os
+.Sh NAME
+.Nm make
+.Nd maintain program dependencies
+.Sh SYNOPSIS
+.Nm make
+.Op Fl Beiknqrst
+.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
+.Op Fl m Ar directory
+.Ek
+.Op Fl V Ar variable
+.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 B
+Try to be backwards compatible by executing a single shell per command and
+by executing the commands to make the sources of a dependency line in sequence.
+.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 (or directories, see the
+.Fl m
+option) 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. Turns compatibility mode off, unless the
+.Ar B
+flag is also specified.
+.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 m Ar directory
+Specify a directory in which to search for sys.mk and makefiles included
+via the <...> style. Multiple directories can be added to form a search path.
+This path will override the default system include path: /usr/share/mk.
+Furthermore the system include path will be appended to the search path used
+for "..."-style inclusions (see the
+.Fl I
+option).
+.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 Fl V Ar variable
+Print
+.Nm make Ns 's
+idea of the value of
+.Ar variable ,
+in the global context.
+Do not build any targets.
+Multiple instances of this option may be specified;
+the variables will be printed one per line,
+with a blank line for each null or undefined variable.
+.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 parentheses
+.Pq Ql ()
+and preceding it with
+a dollar sign
+.Pq Ql \&$ .
+If the variable name contains only a single letter, the surrounding
+braces or parentheses 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.
+.It Ev PWD
+Alternate path to the current directory.
+.Nm make
+normally sets
+.Ql Va .CURDIR
+to the canonical path given by
+.Xr getcwd 2 .
+However, if the environment variable
+.Ql Ev PWD
+is set and gives a path to the current directory, then
+.Nm make
+sets
+.Ql Va .CURDIR
+to the value of
+.Ql Ev PWD
+instead.
+.Ql Ev PWD
+is set to the value of
+.Ql Va .OBJDIR
+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 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.
+.It Ic .WAIT
+If special
+.Ic .WAIT
+source is appears in a dependency line, the sources that precede it are
+made before the sources that succeed it in the line. Loops are not being
+detected and targets that form loops will be silently ignored.
+.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.
+.\" XXX: NOT YET!!!!
+.\" .It Ic .NOTPARALLEL
+.\" The named targets are executed in non parallel mode. If no targets are
+.\" specified, then all targets are executed in non parallel mode.
+.It Ic .NOTPARALLEL
+Disable parallel mode.
+.It Ic .NO_PARALLEL
+Same as above, for compatibility with other pmake variants.
+.It Ic .ORDER
+The named targets are made in sequence.
+.\" XXX: NOT YET!!!!
+.\" .It Ic .PARALLEL
+.\" The named targets are executed in parallel mode. If no targets are
+.\" specified, then all targets are executed in parallel mode.
+.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 .PHONY
+Apply the
+.Ic .PHONY
+attribute to any specified sources. Targets with this attribute are always
+considered to be out of date.
+.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 MACHINE ,
+.Ev MAKE ,
+.Ev MAKEFLAGS ,
+.Ev MAKEOBJDIR ,
+.Ev MAKEOBJDIRPREFIX ,
+and
+.Ev PWD .
+.Sh FILES
+.Bl -tag -width /usr/share/doc/psd/12.make -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
+.It /usr/share/doc/psd/12.make
+PMake tutorial
+.El
+.Sh SEE ALSO
+.Xr mkdep 1
+.Rs
+.%T "PMake - A Tutorial"
+.Re
+.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..c9c4b61
--- /dev/null
+++ b/usr.bin/make/make.c
@@ -0,0 +1,912 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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|OP_PHONY)) {
+ /*
+ * 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 if (gn->type & OP_PHONY) {
+ printf(".PHONY node...");
+ } 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 = NULL;
+
+ if (OP_NOP(cgn->type)) {
+ /*
+ * this node is only source; use the specific pathname for it
+ */
+ child = cgn->path ? cgn->path : cgn->name;
+ }
+ else
+ 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..91e2d3a
--- /dev/null
+++ b/usr.bin/make/make.h
@@ -0,0 +1,372 @@
+/*
+ * 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.
+ *
+ * from: @(#)make.h 8.3 (Berkeley) 6/13/95
+ * $Id$
+ */
+
+/*-
+ * 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>
+#if !defined(MAKE_BOOTSTRAP) && defined(BSD)
+#include <sys/cdefs.h>
+#else
+#ifndef __P
+#if defined(__STDC__) || defined(__cplusplus)
+#define __P(protos) protos /* full-blown ANSI C */
+#else
+#define __P(protos) () /* traditional C preprocessor */
+#endif
+#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) */
+ int order; /* Its wait weight */
+
+ 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 */
+#define OP_PHONY 0x00010000 /* Not a file target; run always */
+/* 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 */
+
+extern Lst sysIncPath; /* The system include path. */
+
+/*
+ * 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..1c56450
--- /dev/null
+++ b/usr.bin/make/nonints.h
@@ -0,0 +1,145 @@
+/*-
+ * 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.
+ *
+ * from: @(#)nonints.h 8.3 (Berkeley) 3/19/94
+ * $Id$
+ */
+
+/* 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 **));
+char *Cmd_Exec __P((char *, 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 *estrdup __P((const char *));
+void *emalloc __P((size_t));
+void *erealloc __P((void *, size_t));
+void enomem __P((void));
+int eunlink __P((const char *));
+
+/* 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..234364f
--- /dev/null
+++ b/usr.bin/make/parse.c
@@ -0,0 +1,2596 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 "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 */
+ Parallel, /* .PARALLEL */
+ ExPath, /* .PATH */
+ Phony, /* .PHONY */
+#ifdef POSIX
+ Posix, /* .POSIX */
+#endif
+ Precious, /* .PRECIOUS */
+ ExShell, /* .SHELL */
+ Silent, /* .SILENT */
+ SingleShell, /* .SINGLESHELL */
+ Suffixes, /* .SUFFIXES */
+ Wait, /* .WAIT */
+ Attribute /* Generic attribute */
+} ParseSpecial;
+
+static ParseSpecial specType;
+static int waiting;
+
+/*
+ * 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 },
+{ ".NO_PARALLEL", NotParallel, 0 },
+{ ".NULL", Null, 0 },
+{ ".OPTIONAL", Attribute, OP_OPTIONAL },
+{ ".ORDER", Order, 0 },
+{ ".PARALLEL", Parallel, 0 },
+{ ".PATH", ExPath, 0 },
+{ ".PHONY", Phony, OP_PHONY },
+#ifdef POSIX
+{ ".POSIX", Posix, 0 },
+#endif
+{ ".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 },
+{ ".WAIT", Wait, 0 },
+};
+
+static int ParseFindKeyword __P((char *));
+static int ParseLinkSrc __P((ClientData, ClientData));
+static int ParseDoOp __P((ClientData, ClientData));
+static int ParseAddDep __P((ClientData, ClientData));
+static void ParseDoSrc __P((int, char *, Lst));
+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);
+}
+
+/*-
+ *---------------------------------------------------------------------
+ * ParseAddDep --
+ * Check if the pair of GNodes given needs to be synchronized.
+ * This has to be when two nodes are on different sides of a
+ * .WAIT directive.
+ *
+ * Results:
+ * Returns 1 if the two targets need to be ordered, 0 otherwise.
+ * If it returns 1, the search can stop
+ *
+ * Side Effects:
+ * A dependency can be added between the two nodes.
+ *
+ *---------------------------------------------------------------------
+ */
+int
+ParseAddDep(pp, sp)
+ ClientData pp;
+ ClientData sp;
+{
+ GNode *p = (GNode *) pp;
+ GNode *s = (GNode *) sp;
+
+ if (p->order < s->order) {
+ /*
+ * XXX: This can cause loops, and loops can cause unmade targets,
+ * but checking is tedious, and the debugging output can show the
+ * problem
+ */
+ (void)Lst_AtEnd(p->successors, (ClientData)s);
+ (void)Lst_AtEnd(s->preds, (ClientData)p);
+ return 0;
+ }
+ else
+ return 1;
+}
+
+
+/*-
+ *---------------------------------------------------------------------
+ * 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, allsrc)
+ int tOp; /* operator (if any) from special targets */
+ char *src; /* name of the source to handle */
+ Lst allsrc; /* List of all sources to wait for */
+{
+ GNode *gn = NULL;
+
+ if (*src == '.' && isupper (src[1])) {
+ int keywd = ParseFindKeyword(src);
+ if (keywd != -1) {
+ int op = parseKeywords[keywd].op;
+ if (op != 0) {
+ Lst_ForEach (targets, ParseDoOp, (ClientData)&op);
+ return;
+ }
+ if (parseKeywords[keywd].spec == Wait) {
+ waiting++;
+ return;
+ }
+ }
+ }
+
+ switch (specType) {
+ case 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)estrdup(src));
+ /*
+ * Add the name to the .TARGETS variable as well, so the user cna
+ * employ that, if desired.
+ */
+ Var_Append(".TARGETS", src, VAR_GLOBAL);
+ return;
+
+ case 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;
+ break;
+
+ default:
+ /*
+ * 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);
+ }
+ }
+ }
+ break;
+ }
+
+ gn->order = waiting;
+ (void)Lst_AtEnd(allsrc, (ClientData)gn);
+ if (waiting) {
+ Lst_ForEach(allsrc, ParseAddDep, (ClientData)gn);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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 archive source names after
+ * expansion */
+ Lst curTargs; /* list of target names to be found and added
+ * to the targets list */
+ Lst curSrcs; /* list of sources in order */
+
+ tOp = 0;
+
+ specType = Not;
+ waiting = 0;
+ paths = (Lst)NULL;
+
+ curTargs = Lst_Init(FALSE);
+ curSrcs = 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.
+ * .PHONY The list of targets
+ * .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;
+#ifdef POSIX
+ case Posix:
+ Var_Set("%POSIX", "1003.2", VAR_GLOBAL);
+ break;
+#endif
+ 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, curSrcs);
+ }
+ Lst_Destroy (sources, NOFREE);
+ cp = line;
+ } else {
+ if (*cp) {
+ *cp = '\0';
+ cp += 1;
+ }
+
+ ParseDoSrc (tOp, line, curSrcs);
+ }
+ 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);
+ }
+
+ /*
+ * Finally, destroy the list of sources
+ */
+ Lst_Destroy(curSrcs, NOFREE);
+}
+
+/*-
+ *---------------------------------------------------------------------
+ * 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 */
+ int level = 0;
+#define ISEQOPERATOR(c) \
+ (((c) == '+') || ((c) == ':') || ((c) == '?') || ((c) == '!'))
+
+ /*
+ * Skip to variable name
+ */
+ for (;(*line == ' ') || (*line == '\t'); line++)
+ continue;
+
+ for (; *line != '=' || level != 0; line++)
+ switch (*line) {
+ case '\0':
+ /*
+ * end-of-line -- can't be a variable assignment.
+ */
+ return FALSE;
+
+ case ' ':
+ case '\t':
+ /*
+ * there can be as much white space as desired so long as there is
+ * only one word before the operator
+ */
+ wasSpace = TRUE;
+ break;
+
+ case '(':
+ case '{':
+ level++;
+ break;
+
+ case '}':
+ case ')':
+ level--;
+ break;
+
+ default:
+ if (wasSpace && haveName) {
+ if (ISEQOPERATOR(*line)) {
+ /*
+ * We must have a finished word
+ */
+ if (level != 0)
+ return FALSE;
+
+ /*
+ * When an = operator [+?!:] is found, the next
+ * character must be an = or it ain't a valid
+ * assignment.
+ */
+ if (line[1] == '=')
+ return haveName;
+#ifdef SUNSHCMD
+ /*
+ * This is a shell command
+ */
+ if (strncmp(line, ":sh", 3) == 0)
+ return haveName;
+#endif
+ }
+ /*
+ * This is the start of another word, so not assignment.
+ */
+ return FALSE;
+ }
+ else {
+ haveName = TRUE;
+ wasSpace = FALSE;
+ }
+ break;
+ }
+
+ 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:
+#ifdef SUNSHCMD
+ while (*opc != ':')
+ if (--opc < line)
+ break;
+
+ if (strncmp(opc, ":sh", 3) == 0) {
+ type = VAR_SHELL;
+ *opc = '\0';
+ break;
+ }
+#endif
+ 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) {
+ Boolean freeCmd = FALSE; /* TRUE if the command needs to be freed, i.e.
+ * if any variable expansion was performed */
+ char *res, *err;
+
+ if (strchr(cp, '$') != 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.
+ */
+ cp = Var_Subst(NULL, cp, VAR_CMD, TRUE);
+ freeCmd = TRUE;
+ }
+
+ res = Cmd_Exec(cp, &err);
+ Var_Set(line, res, ctxt);
+ free(res);
+
+ if (err)
+ Parse_Error(PARSE_WARNING, err, cp);
+
+ if (freeCmd)
+ free(cp);
+ } 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, *Fname;
+
+ /* Make a temporary copy of this, to be safe. */
+ Fname = estrdup(fname);
+
+ prefEnd = strrchr (Fname, '/');
+ if (prefEnd != (char *)NULL) {
+ char *newName;
+
+ *prefEnd = '\0';
+ if (file[0] == '/')
+ newName = estrdup(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;
+ }
+ free (Fname);
+ } 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 = estrdup(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, lineLength = 0;
+ Buffer buf;
+
+ buf = Buf_Init(MAKE_BSIZE);
+
+ do {
+ Buf_Discard(buf, lineLength);
+ lastc = '\0';
+
+ while (((c = ParseReadc()) != '\n' || lastc == '\\')
+ && c != EOF) {
+ if (c == '\n') {
+ Buf_ReplaceLastByte(buf, (Byte)' ');
+ lineno++;
+
+ while ((c = ParseReadc()) == ' ' || c == '\t');
+
+ if (c == EOF)
+ break;
+ }
+
+ Buf_AddByte(buf, (Byte)c);
+ lastc = c;
+ }
+
+ if (c == EOF) {
+ Parse_Error(PARSE_FATAL, "Unclosed conditional/for loop");
+ Buf_Destroy(buf, TRUE);
+ return((char *)NULL);
+ }
+
+ lineno++;
+ Buf_AddByte(buf, (Byte)'\0');
+ line = (char *)Buf_GetAll(buf, &lineLength);
+ } while (skip == 1 && line[0] != '.');
+
+ 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 (
+#if 0
+ compatMake &&
+#endif
+ (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 > line + 1 && 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 \"%s\"",
+ cp);
+ }
+ }
+#ifdef SYSVINCLUDE
+ } else if (strncmp (line, "include", 7) == 0 &&
+ isspace((unsigned char) 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 ()
+{
+ mainNode = NILGNODE;
+ parseIncPath = Lst_Init (FALSE);
+ sysIncPath = Lst_Init (FALSE);
+ includes = Lst_Init (FALSE);
+ targCmds = Lst_Init (FALSE);
+}
+
+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 ("no target to make.");
+ /*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..4405682
--- /dev/null
+++ b/usr.bin/make/pathnames.h
@@ -0,0 +1,41 @@
+/*
+ * 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$
+ */
+
+#define _PATH_OBJDIR "obj"
+#define _PATH_OBJDIRPREFIX "/usr/obj"
+#define _PATH_DEFSHELLDIR "/bin"
+#define _PATH_DEFSYSMK "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..d2646f4
--- /dev/null
+++ b/usr.bin/make/sprite.h
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ *
+ * from: @(#)sprite.h 8.1 (Berkeley) 6/6/93
+ * $Id$
+ */
+
+/*
+ * 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..03ea87a
--- /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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90";
+#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) + 1) * 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 */
+ argv = (char **)erealloc(argv,
+ (argmax + 1) * sizeof(char *));
+ }
+ 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..5d3cf9a
--- /dev/null
+++ b/usr.bin/make/suff.c
@@ -0,0 +1,2449 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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"
+
+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 SuffUnRef __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 = estrdup (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 = estrdup(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 = estrdup(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 = estrdup(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 = estrdup(gn->name);
+ targ->suff = suffNull;
+ targ->suff->refCount++;
+ targ->node = gn;
+ targ->parent = (Src *)NULL;
+ targ->children = 0;
+ targ->pref = estrdup(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) {
+ char *ptr;
+ 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 savep = strlen(gn->path) - targ->suff->nameLen;
+ char savec;
+
+ if (gn->suffix)
+ gn->suffix->refCount--;
+ gn->suffix = targ->suff;
+ gn->suffix->refCount++;
+
+ savec = gn->path[savep];
+ gn->path[savep] = '\0';
+
+ if ((ptr = strrchr(gn->path, '/')) != NULL)
+ ptr++;
+ else
+ ptr = gn->path;
+
+ Var_Set(PREFIX, ptr, gn);
+
+ gn->path[savep] = savec;
+ } else {
+ /*
+ * The .PREFIX gets the full path if the target has
+ * no known suffix.
+ */
+ if (gn->suffix)
+ gn->suffix->refCount--;
+ gn->suffix = NULL;
+
+ if ((ptr = strrchr(gn->path, '/')) != NULL)
+ ptr++;
+ else
+ ptr = gn->path;
+
+ Var_Set(PREFIX, ptr, 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 = estrdup(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 = estrdup(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 = estrdup ("");
+ 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..3fdc65b
--- /dev/null
+++ b/usr.bin/make/targ.c
@@ -0,0 +1,660 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 = estrdup (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->order = 0;
+ 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, %d",
+ parts->tm_hour, parts->tm_min, parts->tm_sec,
+ months[parts->tm_mon], parts->tm_mday, 1900 + 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/util.c b/usr.bin/make/util.c
new file mode 100644
index 0000000..62b0e79
--- /dev/null
+++ b/usr.bin/make/util.c
@@ -0,0 +1,349 @@
+/*
+ * Missing stuff from OS's
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id$";
+#endif
+
+#include <stdio.h>
+#include "make.h"
+
+#if !__STDC__
+# ifndef const
+# define const
+# endif
+#endif
+
+#ifdef sun
+
+
+
+extern int errno, sys_nerr;
+extern char *sys_errlist[];
+
+char *
+strerror(e)
+ int e;
+{
+ static char buf[100];
+ if (e < 0 || e >= sys_nerr) {
+ sprintf(buf, "Unknown error %d", e);
+ return buf;
+ }
+ else
+ return sys_errlist[e];
+}
+#endif
+
+#ifdef ultrix
+#include <string.h>
+
+/* strdup
+ *
+ * Make a duplicate of a string.
+ * For systems which lack this function.
+ */
+char *
+strdup(str)
+ const char *str;
+{
+ size_t len;
+
+ if (str == NULL)
+ return NULL;
+ len = strlen(str) + 1;
+ if ((p = malloc(len)) == NULL)
+ return NULL;
+
+ return memcpy(p, str, len);
+}
+
+#endif
+
+#if defined(sun) || defined(__hpux) || defined(__sgi)
+
+int
+setenv(name, value, dum)
+ const char *name;
+ const char *value;
+ int dum;
+{
+ register char *p;
+ int len = strlen(name) + strlen(value) + 2; /* = \0 */
+ char *ptr = (char*) malloc(len);
+
+ (void) dum;
+
+ if (ptr == NULL)
+ return -1;
+
+ p = ptr;
+
+ while (*name)
+ *p++ = *name++;
+
+ *p++ = '=';
+
+ while (*value)
+ *p++ = *value++;
+
+ *p = '\0';
+
+ len = putenv(ptr);
+/* free(ptr); */
+ return len;
+}
+#endif
+
+#ifdef __hpux
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syscall.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+
+int
+killpg(pid, sig)
+ int pid, sig;
+{
+ return kill(-pid, sig);
+}
+
+void
+srandom(seed)
+ long seed;
+{
+ srand48(seed);
+}
+
+long
+random()
+{
+ return lrand48();
+}
+
+/* turn into bsd signals */
+void (*
+signal(s, a)) ()
+ int s;
+ void (*a)();
+{
+ struct sigvec osv, sv;
+
+ (void) sigvector(s, (struct sigvec *) 0, &osv);
+ sv = osv;
+ sv.sv_handler = a;
+#ifdef SV_BSDSIG
+ sv.sv_flags = SV_BSDSIG;
+#endif
+
+ if (sigvector(s, &sv, (struct sigvec *) 0) == -1)
+ return (BADSIG);
+ return (osv.sv_handler);
+}
+
+#if !defined(BSD) && !defined(d_fileno)
+# define d_fileno d_ino
+#endif
+
+#ifndef DEV_DEV_COMPARE
+# define DEV_DEV_COMPARE(a, b) ((a) == (b))
+#endif
+#define ISDOT(c) ((c)[0] == '.' && (((c)[1] == '\0') || ((c)[1] == '/')))
+#define ISDOTDOT(c) ((c)[0] == '.' && ISDOT(&((c)[1])))
+
+
+/* strrcpy():
+ * Like strcpy, going backwards and returning the new pointer
+ */
+static char *
+strrcpy(ptr, str)
+ register char *ptr, *str;
+{
+ register int len = strlen(str);
+
+ while (len)
+ *--ptr = str[--len];
+
+ return (ptr);
+} /* end strrcpy */
+
+
+char *
+getwd(pathname)
+ char *pathname;
+{
+ DIR *dp;
+ struct dirent *d;
+ extern int errno;
+
+ struct stat st_root, st_cur, st_next, st_dotdot;
+ char pathbuf[MAXPATHLEN], nextpathbuf[MAXPATHLEN * 2];
+ char *pathptr, *nextpathptr, *cur_name_add;
+
+ /* find the inode of root */
+ if (stat("/", &st_root) == -1) {
+ (void) sprintf(pathname,
+ "getwd: Cannot stat \"/\" (%s)", strerror(errno));
+ return (NULL);
+ }
+ pathbuf[MAXPATHLEN - 1] = '\0';
+ pathptr = &pathbuf[MAXPATHLEN - 1];
+ nextpathbuf[MAXPATHLEN - 1] = '\0';
+ cur_name_add = nextpathptr = &nextpathbuf[MAXPATHLEN - 1];
+
+ /* find the inode of the current directory */
+ if (lstat(".", &st_cur) == -1) {
+ (void) sprintf(pathname,
+ "getwd: Cannot stat \".\" (%s)", strerror(errno));
+ return (NULL);
+ }
+ nextpathptr = strrcpy(nextpathptr, "../");
+
+ /* Descend to root */
+ for (;;) {
+
+ /* look if we found root yet */
+ if (st_cur.st_ino == st_root.st_ino &&
+ DEV_DEV_COMPARE(st_cur.st_dev, st_root.st_dev)) {
+ (void) strcpy(pathname, *pathptr != '/' ? "/" : pathptr);
+ return (pathname);
+ }
+
+ /* open the parent directory */
+ if (stat(nextpathptr, &st_dotdot) == -1) {
+ (void) sprintf(pathname,
+ "getwd: Cannot stat directory \"%s\" (%s)",
+ nextpathptr, strerror(errno));
+ return (NULL);
+ }
+ if ((dp = opendir(nextpathptr)) == NULL) {
+ (void) sprintf(pathname,
+ "getwd: Cannot open directory \"%s\" (%s)",
+ nextpathptr, strerror(errno));
+ return (NULL);
+ }
+
+ /* look in the parent for the entry with the same inode */
+ if (DEV_DEV_COMPARE(st_dotdot.st_dev, st_cur.st_dev)) {
+ /* Parent has same device. No need to stat every member */
+ for (d = readdir(dp); d != NULL; d = readdir(dp))
+ if (d->d_fileno == st_cur.st_ino)
+ break;
+ }
+ else {
+ /*
+ * Parent has a different device. This is a mount point so we
+ * need to stat every member
+ */
+ for (d = readdir(dp); d != NULL; d = readdir(dp)) {
+ if (ISDOT(d->d_name) || ISDOTDOT(d->d_name))
+ continue;
+ (void) strcpy(cur_name_add, d->d_name);
+ if (lstat(nextpathptr, &st_next) == -1) {
+ (void) sprintf(pathname, "getwd: Cannot stat \"%s\" (%s)",
+ d->d_name, strerror(errno));
+ (void) closedir(dp);
+ return (NULL);
+ }
+ /* check if we found it yet */
+ if (st_next.st_ino == st_cur.st_ino &&
+ DEV_DEV_COMPARE(st_next.st_dev, st_cur.st_dev))
+ break;
+ }
+ }
+ if (d == NULL) {
+ (void) sprintf(pathname, "getwd: Cannot find \".\" in \"..\"");
+ (void) closedir(dp);
+ return (NULL);
+ }
+ st_cur = st_dotdot;
+ pathptr = strrcpy(pathptr, d->d_name);
+ pathptr = strrcpy(pathptr, "/");
+ nextpathptr = strrcpy(nextpathptr, "../");
+ (void) closedir(dp);
+ *cur_name_add = '\0';
+ }
+} /* end getwd */
+
+
+char *sys_siglist[] = {
+ "Signal 0",
+ "Hangup", /* SIGHUP */
+ "Interrupt", /* SIGINT */
+ "Quit", /* SIGQUIT */
+ "Illegal instruction", /* SIGILL */
+ "Trace/BPT trap", /* SIGTRAP */
+ "IOT trap", /* SIGIOT */
+ "EMT trap", /* SIGEMT */
+ "Floating point exception", /* SIGFPE */
+ "Killed", /* SIGKILL */
+ "Bus error", /* SIGBUS */
+ "Segmentation fault", /* SIGSEGV */
+ "Bad system call", /* SIGSYS */
+ "Broken pipe", /* SIGPIPE */
+ "Alarm clock", /* SIGALRM */
+ "Terminated", /* SIGTERM */
+ "User defined signal 1", /* SIGUSR1 */
+ "User defined signal 2", /* SIGUSR2 */
+ "Child exited", /* SIGCLD */
+ "Power-fail restart", /* SIGPWR */
+ "Virtual timer expired", /* SIGVTALRM */
+ "Profiling timer expired", /* SIGPROF */
+ "I/O possible", /* SIGIO */
+ "Window size changes", /* SIGWINDOW */
+ "Stopped (signal)", /* SIGSTOP */
+ "Stopped", /* SIGTSTP */
+ "Continued", /* SIGCONT */
+ "Stopped (tty input)", /* SIGTTIN */
+ "Stopped (tty output)", /* SIGTTOU */
+ "Urgent I/O condition", /* SIGURG */
+ "Remote lock lost (NFS)", /* SIGLOST */
+ "Signal 31", /* reserved */
+ "DIL signal" /* SIGDIL */
+};
+
+int
+utimes(file, tvp)
+ char *file;
+ struct timeval tvp[2];
+{
+ struct utimbuf t;
+
+ t.actime = tvp[0].tv_sec;
+ t.modtime = tvp[1].tv_sec;
+ return(utime(file, &t));
+}
+
+
+#endif /* __hpux */
+
+#if defined(sun) && defined(__svr4__)
+#include <signal.h>
+
+/* turn into bsd signals */
+void (*
+signal(s, a)) ()
+ int s;
+ void (*a)();
+{
+ struct sigaction sa, osa;
+
+ sa.sa_handler = a;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ if (sigaction(s, &sa, &osa) == -1)
+ return SIG_ERR;
+ else
+ return osa.sa_handler;
+}
+
+#endif
diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c
new file mode 100644
index 0000000..f0d741c
--- /dev/null
+++ b/usr.bin/make/var.c
@@ -0,0 +1,2044 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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 */
+} 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));
+#ifdef SYSVVARSUB
+static Boolean VarSYSVMatch __P((char *, Boolean, Buffer, ClientData));
+#endif
+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 = estrdup(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 = estrdup (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);
+}
+
+#ifdef SYSVVARSUB
+/*-
+ *-----------------------------------------------------------------------
+ * 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);
+}
+#endif
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * 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 (1) { /* substitute in each word of the variable */
+ /*
+ * Break substitution 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
+ * substitutions 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 || (pattern->flags & VAR_SUB_GLOBAL) == 0){
+ done = TRUE;
+ }
+ } 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:
+ * addSpace was set TRUE if characters were added to the buffer.
+ */
+ 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=0; /* 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*/
+#ifdef SUNSHCMD
+ case 's':
+ if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) {
+ char *err;
+ newStr = Cmd_Exec (str, &err);
+ if (err)
+ Error (err, str);
+ cp = tstr + 2;
+ termc = *cp;
+ break;
+ }
+ /*FALLTHRU*/
+#endif
+ default:
+ {
+#ifdef SYSVVARSUB
+ /*
+ * 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
+#endif
+ {
+ 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/makewhatis/makewhatis.local.8 b/usr.bin/makewhatis/makewhatis.local.8
new file mode 100644
index 0000000..b7fd62f
--- /dev/null
+++ b/usr.bin/makewhatis/makewhatis.local.8
@@ -0,0 +1,70 @@
+.\" Copyright (c) April 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: makewhatis.local.8,v 1.5 1997/02/22 15:47:02 peter Exp $
+.Dd April 26, 1996
+.Dt MAKEWHATIS.LOCAL 8
+.Os FreeBSD 2.2
+.Sh NAME
+.Nm makewhatis.local , catman.local
+.Nd start makewhatis for local file systems
+.Sh SYNOPSIS
+.Nm /usr/libexec/makewhatis.local
+.Op options
+.Ar directories ...
+.Nm /usr/libexec/catman.local
+.Op options
+.Ar directories ...
+.Sh DESCRIPTION
+.Nm
+start
+.Xr makewhatis 1
+only for file systems physically mounted on the system
+where the
+.Nm
+is being executed. Running makewhatis
+from
+.Pa /etc/weekly
+for rw nfs-mounted /usr may kill
+your NFS server -- all NFS clients start makewhatis at the same time!
+So use this wrapper for
+.Xr cron 8
+instead of calling makewhatis directly.
+.Sh FILES
+.Bl -tag -width /etc/weekly.XXX -compact
+.It Pa /etc/weekly
+run
+.Nm
+every week
+.El
+.Sh SEE ALSO
+.Xr catman 1 ,
+.Xr find 1 ,
+.Xr makewhatis 1 ,
+.Xr cron 8 .
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.2 .
diff --git a/usr.bin/makewhatis/makewhatis.local.sh b/usr.bin/makewhatis/makewhatis.local.sh
new file mode 100644
index 0000000..9ad997b
--- /dev/null
+++ b/usr.bin/makewhatis/makewhatis.local.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# Copyright (c) April 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# makewhatis.local - start makewhatis(1) only for file systems
+# physically mounted on the system
+#
+# Running makewhatis from /etc/weekly for rw nfs-mounted /usr may kill
+# your NFS server -- all clients start makewhatis at the same time!
+# So use this wrapper instead calling makewhatis directly.
+#
+# PS: this wrapper works also for catman(1)
+#
+# $Id$
+
+PATH=/bin:/usr/bin:$PATH; export PATH
+opt= dirs= localdirs=
+
+for arg
+do
+ case "$arg" in
+ -*) opt="$opt $arg";;
+ *) dirs="$dirs $arg";;
+ esac
+done
+
+dirs=`echo $dirs | sed 's/:/ /g'`
+case X"$dirs" in X) echo "usage: $0 [options] directories ..."; exit 1;; esac
+
+localdirs=`find -H $dirs -fstype local -type d -prune -print`
+
+case X"$localdirs" in
+ X) echo "$0: no local-mounted manual directories found: $dirs"
+ exit 1;;
+ *) exec `basename $0 .local` $opt $localdirs;;
+esac
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..42780fa
--- /dev/null
+++ b/usr.bin/man/config.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 1989, 1993, 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 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.8 (Berkeley) 1/31/95";
+#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);
+ }
+ }
+
+ fclose(cfp);
+}
+
+/*
+ * 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..4f3b9e5
--- /dev/null
+++ b/usr.bin/man/man.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright (c) 1987, 1993, 1994, 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 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, 1995\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)man.c 8.17 (Berkeley) 1/31/95";
+#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..7e96c55
--- /dev/null
+++ b/usr.bin/mesg/mesg.1
@@ -0,0 +1,92 @@
+.\" 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
+.\" $Id: mesg.1,v 1.4 1997/02/22 19:56:08 peter Exp $
+.\"
+.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 v1 .
diff --git a/usr.bin/mesg/mesg.c b/usr.bin/mesg/mesg.c
new file mode 100644
index 0000000..96906c6
--- /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, "")) != -1)
+ 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..0c615ce
--- /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 int l_command_table()
+{
+ last_token = "command_table";
+ return COMMAND_TABLE;
+}
+
+static int l_request()
+{
+ last_token = "request";
+ return REQUEST;
+}
+
+static int l_unimplemented()
+{
+ last_token = "unimplemented";
+ return UNIMPLEMENTED;
+}
+
+static int l_end()
+{
+ last_token = "end";
+ return END;
+}
+
+static int 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 int 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..c1b0d2f
--- /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(), *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..1c3a3fb
--- /dev/null
+++ b/usr.bin/mk_cmds/mk_cmds.c
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+int
+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;
+}
+
+int
+yyerror(s)
+ char *s;
+{
+ fputs(s, stderr);
+ fprintf(stderr, "\nLine %d; last token was '%s'\n",
+ yylineno, last_token);
+ return 0;
+}
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..e46afce
--- /dev/null
+++ b/usr.bin/mk_cmds/utils.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+
+#include <stdlib.h>
+#include "copyright.h"
+#include "ss_internal.h" /* includes stdio and string */
+
+extern FILE *output_file;
+
+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..a094bc5
--- /dev/null
+++ b/usr.bin/mkdep/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+MAN1= mkdep.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/mkdep.gcc.sh ${DESTDIR}${BINDIR}/mkdep
+
+.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.gcc.sh b/usr.bin/mkdep/mkdep.gcc.sh
new file mode 100644
index 0000000..8ad51d0
--- /dev/null
+++ b/usr.bin/mkdep/mkdep.gcc.sh
@@ -0,0 +1,100 @@
+#!/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
+# $Id$
+
+PATH=/bin:/usr/bin; 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
+
+case $# in 0)
+ echo 'usage: mkdep [-ap] [-f file] [flags] file ...' >&2
+ exit 1;;
+esac
+
+TMP=_mkdep$$
+trap 'rm -f $TMP ; exit 1' 1 2 3 13 15
+trap 'rm -f $TMP' 0
+
+# 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"}
+
+echo "# $@" > $TMP # store arguments for debugging
+
+if $MKDEP_CPP -M "$@" >> $TMP; then :
+else
+ echo 'mkdep: compile failed' >&2
+ exit 1
+fi
+
+case x$pflag in
+ x) case $append in
+ 0) sed -e 's; \./; ;g' < $TMP > $D;;
+ *) sed -e 's; \./; ;g' < $TMP >> $D;;
+ esac
+ ;;
+ *) case $append in
+ 0) sed -e 's;\.o:;:;' -e 's; \./; ;g' < $TMP > $D;;
+ *) sed -e 's;\.o:;:;' -e 's; \./; ;g' < $TMP >> $D;;
+ esac
+ ;;
+esac
+
+exit $?
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/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..3aa3ee9
--- /dev/null
+++ b/usr.bin/mkfifo/mkfifo.1
@@ -0,0 +1,77 @@
+.\" 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
+.\" $Id$
+.\"
+.Dd January 5, 1994
+.Dt MKFIFO 1
+.Os BSD 4.4
+.Sh NAME
+.Nm mkfifo
+.Nd make fifos
+.Sh SYNOPSIS
+.Nm
+.Ar fifo_name ...
+.Sh DESCRIPTION
+The
+.Nm
+command creates the fifos requested, in the order specified,
+using mode
+.Li \&0777 .
+.Pp
+The
+.Nm
+command requires write permission in the parent directory.
+.Pp
+The
+.Nm
+command exits 0 if successful, and >0 if an error occurred.
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compliant.
+.Sh SEE ALSO
+.Xr mkdir 1 ,
+.Xr rm 1 ,
+.Xr mkfifo 2 ,
+.Xr mknod 2 ,
+.Xr mknod 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/usr.bin/mkfifo/mkfifo.c b/usr.bin/mkfifo/mkfifo.c
new file mode 100644
index 0000000..ddd9a8b
--- /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, "")) != -1)
+ 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..f07bf46
--- /dev/null
+++ b/usr.bin/mklocale/Makefile
@@ -0,0 +1,10 @@
+# @(#)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
+SUBDIR= data
+
+.include <bsd.prog.mk>
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/Makefile b/usr.bin/mklocale/data/Makefile
new file mode 100644
index 0000000..6416bed
--- /dev/null
+++ b/usr.bin/mklocale/data/Makefile
@@ -0,0 +1,50 @@
+# $Id: Makefile,v 1.8 1997/02/28 22:44:31 adam Exp $
+
+NOMAN=YES
+CLEANFILES+= ${LOCALES:S/$/.out/g}
+
+LOCALES= ja_JP.EUC \
+ ko_KR.EUC \
+ lt_LN.ASCII \
+ lt_LN.ISO_8859-1 \
+ lt_LN.ISO_8859-2 \
+ ru_SU.CP866 \
+ ru_SU.KOI8-R
+
+LOCALEDIR= ${DESTDIR}/usr/share/locale
+
+.if exists(${.OBJDIR}/../mklocale)
+MKLOCALE=${.OBJDIR}/../mklocale
+.else
+MKLOCALE=${.CURDIR}/../mklocale
+.endif
+
+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
+
+LATIN2LINKS = hr_HR
+
+.SUFFIXES: .src .out
+
+.src.out:
+ ${MKLOCALE} -o ${.TARGET} ${.IMPSRC}
+
+all: ${LOCALES:S/$/.out/g}
+
+afterinstall:
+.for locale in ${LOCALES}
+ ${INSTALL} ${COPY} -m 644 -o ${BINOWN} -g ${BINGRP} \
+ ${locale}.out ${LOCALEDIR}/${locale}/LC_CTYPE
+.endfor
+.for link in ${LATIN1LINKS}
+ ln -fs ../lt_LN.ISO_8859-1/LC_CTYPE \
+ ${LOCALEDIR}/${link}.ISO_8859-1/LC_CTYPE
+.endfor
+.for link in ${LATIN2LINKS}
+ ln -fs ../lt_LN.ISO_8859-2/LC_CTYPE \
+ ${LOCALEDIR}/${link}.ISO_8859-2/LC_CTYPE
+.endfor
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/mklocale/data/ja_JP.EUC.src b/usr.bin/mklocale/data/ja_JP.EUC.src
new file mode 100644
index 0000000..88fdc91
--- /dev/null
+++ b/usr.bin/mklocale/data/ja_JP.EUC.src
@@ -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/ko_KR.EUC.src b/usr.bin/mklocale/data/ko_KR.EUC.src
new file mode 100644
index 0000000..ce54ab3
--- /dev/null
+++ b/usr.bin/mklocale/data/ko_KR.EUC.src
@@ -0,0 +1,119 @@
+/*
+ * Korean LC_CTYPE definitions using EUC-KR character sets
+ * (ko_KR.EUC.src)
+ *
+ * Choi Jun Ho, junker@jazz.snu.ac.kr
+ * NARAE, Seoul National Univ., CS Dept.
+ * Last Updated on Mar 24 1997
+ *
+ * It is based on manpage mklocale(1), euc(4), ja_JP.EUC.src.
+ *
+ */
+
+ENCODING "EUC"
+
+/* EUC-KR(KS C 5601.1992)
+ * 0xa1a1-0xfefe
+ * byte 1: 0xa1-0xfe
+ * byte 2: 0xa1-0xfe
+ */
+
+/* We have only codeset 1 and 2, so others are dummy.
+ But it must be defined to work in 2.2 xpg4 locale routine...
+ */
+VARIABLE 1 0x0000 2 0x8080 2 0x0080 3 0x8000 0x8080
+
+/*
+ * Code Set 1, US-ASCII equivalent
+ */
+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' > < 'a' - 'z' : 'a' >
+MAPUPPER < 'A' - 'Z' : 'A' > < 'a' - 'z' : 'A' >
+TODIGIT < '0' - '9' : 0 >
+TODIGIT < 'A' - 'F' : 10 > < 'a' - 'f' : 10 >
+
+/*
+ * Code Set 2, EUC-KR
+ */
+ALPHA 0xa3c1 - 0xa3da 0xa3e1 - 0xa3fa
+DIGIT 0xa3b0 - 0xa3b9
+UPPER 0xa3c1 - 0xa3da
+LOWER 0xa3e1 - 0xa3fa
+PUNCT 0xa3a1 - 0xa3af 0xa3ba - 0xa3c0 0xa3db - 0xa3e0 0xa3fb - 0xa3fe
+SPACE 0xa1a1
+XDIGIT 0xa3b0 - 0xa3b9 0xa3c1 - 0xa3c6 0xa3e1 - 0xa3e6
+BLANK 0xa1a1
+PRINT 0xa1a1 - 0xfefe
+SPECIAL 0xa1a2 - 0xa2e5
+
+MAPLOWER < 0xa3c1 - 0xa3da : 0xa3e1 > < 0xa3e1 - 0xa3fa : 0xa3e1 >
+MAPUPPER < 0xa3c1 - 0xa3da : 0xa3c1 > < 0xa3b0 - 0xa3b9 : 0xa3c1 >
+TODIGIT < 0xa3b0 - 0xa3b9 : 0 >
+TODIGIT < 0xa3c1 - 0xa3c6 : 10 > < 0xa3e1 - 0xa3e6 : 10 >
+
+
+UPPER 0xa5c1 - 0xa5d8 /* Greek */
+LOWER 0xa5e1 - 0xa5f8 /* Greek */
+MAPLOWER < 0xa5c1 - 0xa5d8 : 0xa5e1 > < 0xa5e1 - 0xa5f8 : 0xa5e1 >
+MAPUPPER < 0xa5c1 - 0xa5d8 : 0xa5c1 > < 0xa5e1 - 0xa5f8 : 0xa5c1 >
+
+UPPER 0xaca1 - 0xacc1 /* Cyrillic */
+LOWER 0xacd1 - 0xacf1 /* Cyrillic */
+MAPLOWER < 0xaca1 - 0xacc1 : 0xacd1 > < 0xacd1 - 0xacf1 : 0xacd1 >
+MAPUPPER < 0xaca1 - 0xacc1 : 0xaca1 > < 0xacd1 - 0xacf1 : 0xaca1 >
+
+DIGIT 0xa5a1 - 0xa5aa 0xa5b0 - 0xa5b9 /* Greek Digit */
+SPECIAL 0xa6a1 - 0xa6e4 0xa7a1 - 0xa7ef /* Symbols */
+SPECIAL 0xa8a1 - 0xa8fe 0xa9a1 - 0xa9fe /* Circle Symbols */
+
+PHONOGRAM 0xa4a1 - 0xa4fe /* Full-width Hangul glyph */
+PHONOGRAM 0xaaa1 - 0xaaf3 /* Full-width Hirakana */
+PHONOGRAM 0xaba1 - 0xabf6 /* Full-width Katakana */
+
+PHONOGRAM 0xb0a1 - 0xb0fe 0xb1a1 - 0xb1fe 0xb2a1 - 0xb2fe
+PHONOGRAM 0xb3a1 - 0xb3fe 0xb4a1 - 0xb4fe 0xb5a1 - 0xb5fe
+PHONOGRAM 0xb6a1 - 0xb6fe 0xb7a1 - 0xb7fe 0xb8a1 - 0xb8fe
+PHONOGRAM 0xb9a1 - 0xb9fe 0xbaa1 - 0xbafe 0xbba1 - 0xbbfe
+PHONOGRAM 0xbca1 - 0xbcfe 0xbda1 - 0xbdfe 0xbea1 - 0xbefe
+PHONOGRAM 0xbfa1 - 0xbffe 0xc0a1 - 0xc0fe 0xc1a1 - 0xc1fe
+PHONOGRAM 0xc2a1 - 0xc2fe 0xc3a1 - 0xc3fe 0xc4a1 - 0xc4fe
+PHONOGRAM 0xc5a1 - 0xc5fe 0xc6a1 - 0xc6fe 0xc7a1 - 0xc7fe
+PHONOGRAM 0xc8a1 - 0xc8fe /* Hangul composed */
+
+IDEOGRAM 0xcaa1 - 0xcafe 0xcba1 - 0xcbfe 0xcca1 - 0xccfe
+IDEOGRAM 0xcda1 - 0xcdfe 0xcea1 - 0xcefe 0xcfa1 - 0xcffe
+IDEOGRAM 0xd0a1 - 0xd0fe 0xd1a1 - 0xd1fe 0xd2a1 - 0xd2fe
+IDEOGRAM 0xd3a1 - 0xd3fe 0xd4a1 - 0xd4fe 0xd5a1 - 0xd5fe
+IDEOGRAM 0xd6a1 - 0xd6fe 0xd7a1 - 0xd7fe 0xd8a1 - 0xd8fe
+IDEOGRAM 0xd9a1 - 0xd9fe 0xdaa1 - 0xdafe 0xdba1 - 0xdbfe
+IDEOGRAM 0xdca1 - 0xdcfe 0xdda1 - 0xddfe 0xdea1 - 0xdefe
+IDEOGRAM 0xdfa1 - 0xdffe 0xe0a1 - 0xe0fe 0xe1a1 - 0xe1fe
+IDEOGRAM 0xe2a1 - 0xe2fe 0xe3a1 - 0xe3fe 0xe4a1 - 0xe4fe
+IDEOGRAM 0xe5a1 - 0xe5fe 0xe6a1 - 0xe6fe 0xe7a1 - 0xe7fe
+IDEOGRAM 0xe8a1 - 0xe8fe 0xe9a1 - 0xe9fe 0xeaa1 - 0xeafe
+IDEOGRAM 0xeba1 - 0xebfe 0xeca1 - 0xecfe 0xeda1 - 0xedfe
+IDEOGRAM 0xeea1 - 0xeefe 0xefa1 - 0xeffe 0xf0a1 - 0xf0fe
+IDEOGRAM 0xf1a1 - 0xf1fe 0xf2a1 - 0xf2fe 0xf3a1 - 0xf3fe
+IDEOGRAM 0xf4a1 - 0xf4fe 0xf5a1 - 0xf5fe 0xf6a1 - 0xf6fe
+IDEOGRAM 0xf7a1 - 0xf7fe 0xf8a1 - 0xf8fe 0xf9a1 - 0xf9fe
+IDEOGRAM 0xfaa1 - 0xfafe 0xfba1 - 0xfbfe 0xfca1 - 0xfcfe
+IDEOGRAM 0xfda1 - 0xfdfe /* Hanja */
+
+/* We don't have codeset 3 and 4.
+ So codeset 3 is only dummy definition
+ */
+PRINT 0xa1 - 0xfe
+SPECIAL 0xa1 - 0xfe
+
+/* End of LC_CTYPE definition */
diff --git a/usr.bin/mklocale/data/lt_LN.ASCII.src b/usr.bin/mklocale/data/lt_LN.ASCII.src
new file mode 100644
index 0000000..95cbeeb
--- /dev/null
+++ b/usr.bin/mklocale/data/lt_LN.ASCII.src
@@ -0,0 +1,28 @@
+/*
+ * Standard LOCALE_CTYPE for the ASCII Locale
+ */
+ENCODING "NONE"
+VARIABLE Strict 7bit ASCII locale
+
+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/data/lt_LN.ISO_8859-1.src b/usr.bin/mklocale/data/lt_LN.ISO_8859-1.src
new file mode 100644
index 0000000..82faff3
--- /dev/null
+++ b/usr.bin/mklocale/data/lt_LN.ISO_8859-1.src
@@ -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 0xa1 - 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' 0xa0
+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/lt_LN.ISO_8859-2.src b/usr.bin/mklocale/data/lt_LN.ISO_8859-2.src
new file mode 100644
index 0000000..3f9ad91
--- /dev/null
+++ b/usr.bin/mklocale/data/lt_LN.ISO_8859-2.src
@@ -0,0 +1,79 @@
+/*
+ * LOCALE_CTYPE for the iso_8859_2 Locale
+ *
+ * $Id$
+ */
+
+ENCODING "NONE"
+VARIABLE ISO 8859-2 Latin-2 character set
+
+#
+# This is a comment
+#
+ALPHA 'A' - 'Z' 'a' - 'z'
+ 0xa1 0xa3 0xa5 0xa6 0xa9 - 0xac 0xae 0xaf
+ 0xb1 0xb3 0xb5 0xb6 0xb9 - 0xbc 0xbe 0xbf
+ 0xc0 - 0xd6 0xd8 - 0xf6 0xf8 - 0xfe
+CONTROL 0x00 - 0x1f 0x7f - 0x9f
+DIGIT '0' - '9'
+GRAPH 0x21 - 0x7e 0xa0 - 0xff
+LOWER 'a' - 'z'
+ 0xb1 0xb3 0xb5 0xb6 0xb9 - 0xbc 0xbe 0xbf
+ 0xdf - 0xf6 0xf8 - 0xfe
+PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e
+ 0xa2 0xa4 0xa7 0xa8 0xad 0xb0 0xb2 0xb4 0xb7 0xb8 0xbd
+ 0xd7 0xf7 0xff
+SPACE 0x09 - 0x0d 0x20 0xa0
+UPPER 'A' - 'Z'
+ 0xa1 0xa3 0xa5 0xa6 0xa9 - 0xac 0xae 0xaf
+ 0xc0 - 0xd6 0xd8 - 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 <0xa1 0xb1>
+MAPLOWER <0xa3 0xb3>
+MAPLOWER <0xa5 0xb5>
+MAPLOWER <0xa6 0xb6>
+MAPLOWER <0xa9 - 0xac : 0xb9>
+MAPLOWER <0xae 0xbe>
+MAPLOWER <0xaf 0xbf>
+MAPLOWER <0xb1 0xb1>
+MAPLOWER <0xb3 0xb3>
+MAPLOWER <0xb5 0xb5>
+MAPLOWER <0xb6 0xb6>
+MAPLOWER <0xb9 - 0xbc : 0xb9>
+MAPLOWER <0xbe 0xbe>
+MAPLOWER <0xbf 0xbf>
+MAPLOWER <0xc0 - 0xd6 : 0xe0>
+MAPLOWER <0xd8 - 0xde : 0xf8>
+MAPLOWER <0xdf - 0xf6 : 0xdf>
+MAPLOWER <0xf8 - 0xfe : 0xf8>
+MAPUPPER <'A' - 'Z' : 'A'>
+MAPUPPER <'a' - 'z' : 'A'>
+MAPUPPER <0xa1 0xa1>
+MAPUPPER <0xa3 0xa3>
+MAPUPPER <0xa5 0xa5>
+MAPUPPER <0xa6 0xa6>
+MAPUPPER <0xa9 - 0xac : 0xa9>
+MAPUPPER <0xae 0xae>
+MAPUPPER <0xaf 0xaf>
+MAPUPPER <0xb1 0xa1>
+MAPUPPER <0xb3 0xa3>
+MAPUPPER <0xb5 0xa5>
+MAPUPPER <0xb6 0xa6>
+MAPUPPER <0xb9 - 0xbc : 0xa9>
+MAPUPPER <0xbe 0xae>
+MAPUPPER <0xbf 0xaf>
+MAPUPPER <0xc0 - 0xd6 : 0xc0>
+MAPUPPER <0xd8 - 0xdf : 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.src b/usr.bin/mklocale/data/ru_SU.CP866.src
new file mode 100644
index 0000000..659482b
--- /dev/null
+++ b/usr.bin/mklocale/data/ru_SU.CP866.src
@@ -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.src b/usr.bin/mklocale/data/ru_SU.KOI8-R.src
new file mode 100644
index 0000000..b9582fe
--- /dev/null
+++ b/usr.bin/mklocale/data/ru_SU.KOI8-R.src
@@ -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 - 0x99 0x9b - 0xff
+LOWER 'a' - 'z' 0xa3 0xc0 - 0xdf
+PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e
+SPACE 0x09 - 0x0d 0x20 0x9a
+UPPER 'A' - 'Z' 0xb3 0xe0 - 0xff
+XDIGIT '0' - '9' 'a' - 'f' 'A' - 'F'
+BLANK ' ' '\t' 0x9a
+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/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..8b59b4b
--- /dev/null
+++ b/usr.bin/mklocale/mklocale.1
@@ -0,0 +1,258 @@
+.\" 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 mklocale 1 ,
+.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/yacc.y b/usr.bin/mklocale/yacc.y
new file mode 100644
index 0000000..760d112
--- /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 = list->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..7a53af9
--- /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 xstr 1 ,
+.Xr lseek 2
+.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..5e238ce
--- /dev/null
+++ b/usr.bin/modstat/Makefile
@@ -0,0 +1,42 @@
+#
+# 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$
+#
+
+PROG= modstat
+MAN8= modstat.8
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/modstat/modstat.8 b/usr.bin/modstat/modstat.8
new file mode 100644
index 0000000..359a856
--- /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$
+.\"
+.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 modload 8 ,
+.Xr modunload 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..a11f4d5
--- /dev/null
+++ b/usr.bin/modstat/modstat.c
@@ -0,0 +1,180 @@
+/*
+ * 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.7 1997/03/11 14:41:52 peter Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.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;
+
+ sbuf.name[MAXLKMNAME - 1] = '\0'; /* In case strncpy limits the string. */
+ if (modname != NULL)
+ strncpy(sbuf.name, modname, MAXLKMNAME - 1);
+
+ 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:")) != -1) {
+ 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..e63efcf
--- /dev/null
+++ b/usr.bin/more/Makefile
@@ -0,0 +1,16 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id$
+
+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..2b8d3a4
--- /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 && 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..26be68b
--- /dev/null
+++ b/usr.bin/more/main.c
@@ -0,0 +1,372 @@
+/*
+ * 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 <stdlib.h>
+#include <locale.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];
+
+ (void) setlocale(LC_ALL, "");
+
+ /*
+ * 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("-");
+ if (file >= 0)
+ 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();
+
+ 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..3355c04
--- /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.
+.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 BUGS
+Incorrect output can result from omitting the -u flag when accessing regular
+files with CRLF line termination.
+.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..a95e700
--- /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")) != -1)
+ 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..5b0a562
--- /dev/null
+++ b/usr.bin/more/output.c
@@ -0,0 +1,258 @@
+/*
+ * 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 <ctype.h>
+#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..2a763c2
--- /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 mail 1 ,
+.Xr more 1 ,
+.Xr aliases 5
+.\".Xr crontab 5 ,
+.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..9af8f58
--- /dev/null
+++ b/usr.bin/msgs/msgs.c
@@ -0,0 +1,863 @@
+/*-
+ * 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/stat.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <locale.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 0644 /* 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 = 0, nextmsg = 0, lastmsg = 0;
+ int blast = 0;
+ FILE *bounds;
+
+#ifdef UNBUFFERED
+ setbuf(stdout, NULL);
+#endif
+ setlocale(LC_ALL, "");
+
+ 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 dirent *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, CMODE);
+
+ 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)
+unsigned char *s;
+{
+ if (*s) while (*s && !isspace(*s)) s++; /* skip over this field */
+ if (*s) while (*s && isspace(*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..54df097
--- /dev/null
+++ b/usr.bin/mt/mt.1
@@ -0,0 +1,226 @@
+.\" 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 retension
+Re-tension the tape
+(one full wind forth and back, 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
+.Fx 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..ae94971
--- /dev/null
+++ b/usr.bin/mt/mt.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[] = "@(#)mt.c 8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+/*
+ * mt --
+ * magnetic tape manipulation program
+ */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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 },
+ { "retension", MTRETENS, 1 },
+#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:")) != -1)
+ 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
+
+#ifdef __FreeBSD__
+#include <machine/wtio.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 weird. 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", WTDS_BITS, WTER_BITS },
+ { MT_ISMFOUR, "Wangtek", WTDS_BITS, WTER_BITS },
+#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", (unsigned short)bp->mt_dsreg, mt->t_dsbits);
+ printreg("\ner", (unsigned short)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/netstat/Makefile b/usr.bin/netstat/Makefile
new file mode 100644
index 0000000..adf1916
--- /dev/null
+++ b/usr.bin/netstat/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/12/93
+
+PROG= netstat
+SRCS= if.c inet.c main.c mbuf.c mroute.c ipx.c route.c \
+ unix.c atalk.c # iso.c ns.c tp_astring.c
+
+CFLAGS+=-I/sys # -g
+#.PATH: ${.CURDIR}/../../sys/netiso
+BINGRP= kmem
+BINMODE=2555
+DPADD= ${LIBKVM} ${LIBIPX}
+LDADD= -lkvm -lipx
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/netstat/atalk.c b/usr.bin/netstat/atalk.c
new file mode 100644
index 0000000..e1fe34d
--- /dev/null
+++ b/usr.bin/netstat/atalk.c
@@ -0,0 +1,285 @@
+/*
+ * 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[] = "@(#)atalk.c 1.1 (Whistle) 6/6/96";
+*/
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+
+#include <net/route.h>
+
+#include <netatalk/at.h>
+#include <netatalk/ddp_var.h>
+
+#include <nlist.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "netstat.h"
+
+struct ddpcb ddpcb;
+struct socket sockb;
+
+static void atalk_erputil __P((int, int));
+
+static int first = 1;
+
+/*
+ * Print a summary of connections related to a Network Systems
+ * protocol. For XXX, also give state of connection.
+ * Listening processes (aflag) are suppressed unless the
+ * -a (all) flag is specified.
+ */
+
+static char *
+at_pr_net(struct sockaddr_at *sat, int numeric)
+{
+static char mybuf[50];
+
+ if (!numeric) {
+ switch(sat->sat_addr.s_net) {
+ case 0xffff:
+ return "????";
+ case ATADDR_ANYNET:
+ return("*");
+ }
+ }
+ sprintf(mybuf,"%hu",ntohs(sat->sat_addr.s_net));
+ return mybuf;
+}
+
+static char *
+at_pr_host(struct sockaddr_at *sat, int numeric)
+{
+static char mybuf[50];
+
+ if (!numeric) {
+ switch(sat->sat_addr.s_node) {
+ case ATADDR_BCAST:
+ return "bcast";
+ case ATADDR_ANYNODE:
+ return("*");
+ }
+ }
+ sprintf(mybuf,"%d",(unsigned int)sat->sat_addr.s_node);
+ return mybuf;
+}
+
+char *
+at_pr_port(struct sockaddr_at *sat)
+{
+static char mybuf[50];
+
+ switch(sat->sat_port) {
+ case ATADDR_ANYPORT:
+ return("*");
+ case 0xff:
+ return "????";
+ default:
+ sprintf(mybuf,"%d",(unsigned int)sat->sat_port);
+ }
+ return mybuf;
+}
+
+static char *
+at_pr_range(struct sockaddr_at *sat)
+{
+static char mybuf[50];
+
+ if(sat->sat_range.r_netrange.nr_firstnet
+ != sat->sat_range.r_netrange.nr_lastnet) {
+ sprintf(mybuf,"%d-%d",
+ ntohs(sat->sat_range.r_netrange.nr_firstnet),
+ ntohs(sat->sat_range.r_netrange.nr_lastnet));
+ } else {
+ sprintf(mybuf,"%d",
+ ntohs(sat->sat_range.r_netrange.nr_firstnet));
+ }
+ return mybuf;
+}
+
+
+/* what == 0 for addr only == 3 */
+/* 1 for net */
+/* 2 for host */
+/* 4 for port */
+/* 8 for numeric only */
+char *
+atalk_print(sa,what)
+ register struct sockaddr *sa;
+{
+ struct sockaddr_at *sat = (struct sockaddr_at *)sa;
+ static char mybuf[50];
+ int numeric = (what & 0x08);
+
+ mybuf[0] = 0;
+ switch (what & 0x13) {
+ case 0:
+ mybuf[0] = 0;
+ break;
+ case 1:
+ sprintf(mybuf,"%s",at_pr_net(sat, numeric));
+ break;
+ case 2:
+ sprintf(mybuf,"%s",at_pr_host(sat, numeric));
+ break;
+ case 3:
+ sprintf(mybuf,"%s.%s",
+ at_pr_net(sat, numeric),
+ at_pr_host(sat, numeric));
+ break;
+ case 0x10:
+ sprintf(mybuf,"%s", at_pr_range(sat));
+ }
+ if (what & 4) {
+ sprintf(mybuf+strlen(mybuf),".%s",at_pr_port(sat));
+ }
+ return mybuf;
+}
+
+char *
+atalk_print2(struct sockaddr *sa, struct sockaddr *mask, int what)
+{
+ int n;
+ static char buf[100];
+ struct sockaddr_at *sat1, *sat2;
+ struct sockaddr_at thesockaddr;
+ struct sockaddr *sa2;
+
+ sat1 = (struct sockaddr_at *)sa;
+ sat2 = (struct sockaddr_at *)mask;
+ sa2 = (struct sockaddr *)&thesockaddr;
+
+ thesockaddr.sat_addr.s_net = sat1->sat_addr.s_net & sat2->sat_addr.s_net;
+ n = snprintf(buf, sizeof(buf), "%s", atalk_print(sa2, 1 |(what & 8)));
+ if(sat2->sat_addr.s_net != 0xFFFF) {
+ thesockaddr.sat_addr.s_net = sat1->sat_addr.s_net | ~sat2->sat_addr.s_net;
+ n += snprintf(buf + n, sizeof(buf) - n,
+ "-%s", atalk_print(sa2, 1 |(what & 8)));
+ }
+ if(what & 2)
+ n += snprintf(buf + n, sizeof(buf) - n, ".%s", atalk_print(sa, what&(~1)));
+ return(buf);
+}
+
+void
+atalkprotopr(off, name)
+ u_long off;
+ char *name;
+{
+ struct ddpcb cb;
+ register struct ddpcb *prev, *next;
+ struct ddpcb *initial;
+
+ if (off == 0)
+ return;
+ kread(off, (char *)&initial, sizeof (struct ddpcb *));
+ ddpcb = cb;
+ prev = (struct ddpcb *)off;
+ for (next = initial ;next != NULL; prev = next) {
+ u_long ppcb;
+
+ kread((u_long)next, (char *)&ddpcb, sizeof (ddpcb));
+ next = ddpcb.ddp_next;
+#if 0
+ if (!aflag && atalk_nullhost(ddpcb.ddp_lsat) ) {
+ continue;
+ }
+#endif
+ kread((u_long)ddpcb.ddp_socket,
+ (char *)&sockb, sizeof (sockb));
+ if (first) {
+ printf("Active ATALK 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(Aflag?" %-18.18s":" %-22.22s", atalk_print(
+ (struct sockaddr *)&ddpcb.ddp_lsat,7));
+ printf(Aflag?" %-18.18s":" %-22.22s", atalk_print(
+ (struct sockaddr *)&ddpcb.ddp_fsat,7));
+ putchar('\n');
+ }
+}
+#define ANY(x,y,z) \
+ ((x) ? printf("\t%d %s%s%s\n",x,y,plural(x),z) : 0)
+
+/*
+ * Dump DDP statistics structure.
+ */
+void
+ddp_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct ddpstat ddpstat;
+
+ if (off == 0)
+ return;
+ kread(off, (char *)&ddpstat, sizeof (ddpstat));
+ printf("%s:\n", name);
+ ANY(ddpstat.ddps_short, "packet", " with short headers ");
+ ANY(ddpstat.ddps_long, "packet", " with long headers ");
+ ANY(ddpstat.ddps_nosum, "packet", " with no checksum ");
+ ANY(ddpstat.ddps_tooshort, "packet", " too short ");
+ ANY(ddpstat.ddps_badsum, "packet", " with bad checksum ");
+ ANY(ddpstat.ddps_toosmall, "packet", " with not enough data ");
+ ANY(ddpstat.ddps_forward, "packet", " forwarded ");
+ ANY(ddpstat.ddps_encap, "packet", " encapsulated ");
+ ANY(ddpstat.ddps_cantforward, "packet", " rcvd for unreachable dest ");
+ ANY(ddpstat.ddps_nosockspace, "packet", " dropped due to no socket space ");
+}
+#undef ANY
+
+
diff --git a/usr.bin/netstat/if.c b/usr.bin/netstat/if.c
new file mode 100644
index 0000000..ca12cda
--- /dev/null
+++ b/usr.bin/netstat/if.c
@@ -0,0 +1,501 @@
+/*
+ * 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.3 (Berkeley) 4/28/95";
+*/
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netipx/ipx.h>
+#include <netipx/ipx_if.h>
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+#ifdef ISO
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#endif
+#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;
+ struct ifnethead ifnethead;
+ union {
+ struct ifaddr ifa;
+ struct in_ifaddr in;
+ struct ipx_ifaddr ipx;
+#ifdef NS
+ struct ns_ifaddr ns;
+#endif
+#ifdef ISO
+ struct iso_ifaddr iso;
+#endif
+ } ifaddr;
+ u_long ifaddraddr;
+ u_long ifaddrfound;
+ u_long ifnetfound;
+ 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 *)&ifnethead, sizeof ifnethead))
+ return;
+ ifnetaddr = (u_long)ifnethead.tqh_first;
+ if (kread(ifnetaddr, (char *)&ifnet, sizeof ifnet))
+ return;
+
+ printf("%-5.5s %-5.5s %-13.13s %-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) {
+ ifnetfound = ifnetaddr;
+ if (kread(ifnetaddr, (char *)&ifnet, sizeof ifnet) ||
+ kread((u_long)ifnet.if_name, tname, 16))
+ return;
+ tname[15] = '\0';
+ ifnetaddr = (u_long)ifnet.if_link.tqe_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_addrhead.tqh_first;
+ }
+ printf("%-5.5s %-5lu ", name, ifnet.if_mtu);
+ ifaddrfound = ifaddraddr;
+ if (ifaddraddr == 0) {
+ printf("%-13.13s ", "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("%-13.13s ", "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("%-13.13s ", netname(in.s_addr,
+ ifaddr.in.ia_subnetmask));
+#else
+ printf("%-13.13s ",
+ netname(htonl(ifaddr.in.ia_subnet),
+ ifaddr.in.ia_subnetmask));
+#endif
+ printf("%-15.15s ",
+ routename(sin->sin_addr.s_addr));
+ break;
+ case AF_IPX:
+ {
+ struct sockaddr_ipx *sipx =
+ (struct sockaddr_ipx *)sa;
+ u_long net;
+ char netnum[10];
+
+ *(union ipx_net *) &net = sipx->sipx_addr.x_net;
+ sprintf(netnum, "%lx", ntohl(net));
+ printf("ipx:%-8s ", netnum);
+/* printf("ipx:%-8s ", netname(net, 0L)); */
+ printf("%-15s ",
+ ipx_phost((struct sockaddr *)sipx));
+ }
+ break;
+
+ case AF_APPLETALK:
+ printf("atalk:%-12.12s ",atalk_print(sa,0x10) );
+ printf("%-9.9s ",atalk_print(sa,0x0b) );
+ break;
+#ifdef NS
+ case AF_NS:
+ {
+ struct sockaddr_ns *sns =
+ (struct sockaddr_ns *)sa;
+ u_long net;
+ char netnum[10];
+
+ *(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;
+#endif
+ case AF_LINK:
+ {
+ struct sockaddr_dl *sdl =
+ (struct sockaddr_dl *)sa;
+ cp = (char *)LLADDR(sdl);
+ n = sdl->sdl_alen;
+ }
+ m = printf("%-11.11s ", "<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 = 30 - m;
+ while (m-- > 0)
+ putchar(' ');
+ break;
+ }
+ ifaddraddr = (u_long)ifaddr.ifa.ifa_link.tqe_next;
+ }
+ printf("%8lu %5lu ",
+ ifnet.if_ipackets, ifnet.if_ierrors);
+ if (bflag)
+ printf("%10lu ", ifnet.if_ibytes);
+ printf("%8lu %5lu ",
+ ifnet.if_opackets, ifnet.if_oerrors);
+ if (bflag)
+ printf("%10lu ", ifnet.if_obytes);
+ printf("%5lu", ifnet.if_collisions);
+ if (tflag)
+ printf(" %3d", ifnet.if_timer);
+ if (dflag)
+ printf(" %3d", ifnet.if_snd.ifq_drops);
+ putchar('\n');
+ if (aflag && ifaddrfound) {
+ /*
+ * Print family's multicast addresses
+ */
+ u_long multiaddr;
+ struct ifmultiaddr ifma;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_dl dl;
+ } msa;
+ const char *fmt;
+
+ for(multiaddr = (u_long)ifnet.if_multiaddrs.lh_first;
+ multiaddr;
+ multiaddr = (u_long)ifma.ifma_link.le_next) {
+ if (kread(multiaddr, (char *)&ifma,
+ sizeof ifma))
+ break;
+ if (kread((u_long)ifma.ifma_addr, (char *)&msa,
+ sizeof msa))
+ break;
+ if (msa.sa.sa_family != sa->sa_family)
+ continue;
+
+ fmt = 0;
+ switch (msa.sa.sa_family) {
+ case AF_INET:
+ fmt = routename(msa.in.sin_addr.s_addr);
+ break;
+
+ case AF_LINK:
+ switch (ifnet.if_type) {
+ case IFT_ETHER:
+ case IFT_FDDI:
+ fmt = ether_ntoa(
+ (struct ether_addr *)
+ LLADDR(&msa.dl));
+ break;
+ }
+ break;
+ }
+ if (fmt)
+ printf("%23s %s\n", "", fmt);
+ }
+ }
+ }
+}
+
+#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.
+ * XXX - should be rewritten to use ifmib(4).
+ */
+static void
+sidewaysintpr(interval, off)
+ unsigned interval;
+ u_long off;
+{
+ struct ifnet ifnet;
+ u_long firstifnet;
+ struct ifnethead ifnethead;
+ register struct iftot *ip, *total;
+ register int line;
+ struct iftot *lastif, *sum, *interesting;
+ int oldmask, first;
+ u_long interesting_off;
+
+ if (kread(off, (char *)&ifnethead, sizeof ifnethead))
+ return;
+ firstifnet = (u_long)ifnethead.tqh_first;
+
+ lastif = iftot;
+ sum = iftot + MAXIF - 1;
+ total = sum - 1;
+ interesting = NULL;
+ interesting_off = 0;
+ for (off = firstifnet, ip = iftot; off;) {
+ 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;
+ interesting_off = off;
+ }
+ snprintf(ip->ift_name, 16, "(%s)", name);;
+ ip++;
+ if (ip >= iftot + MAXIF - 2)
+ break;
+ off = (u_long) ifnet.if_link.tqe_next;
+ }
+ lastif = ip;
+
+ (void)signal(SIGALRM, catchalarm);
+ signalled = NO;
+ (void)alarm(interval);
+ for (ip = iftot; ip < iftot + MAXIF; ip++) {
+ ip->ift_ip = 0;
+ ip->ift_ie = 0;
+ ip->ift_ib = 0;
+ ip->ift_op = 0;
+ ip->ift_oe = 0;
+ ip->ift_ob = 0;
+ ip->ift_co = 0;
+ ip->ift_dr = 0;
+ }
+ first = 1;
+banner:
+ printf("%17s %14s %16s", "input",
+ interesting ? interesting->ift_name : "(Total)", "output");
+ putchar('\n');
+ printf("%10s %5s %10s %10s %5s %10s %5s",
+ "packets", "errs", "bytes", "packets", "errs", "bytes", "colls");
+ if (dflag)
+ printf(" %5.5s", "drops");
+ putchar('\n');
+ fflush(stdout);
+ line = 0;
+loop:
+ if (interesting != NULL) {
+ ip = interesting;
+ if (kread(interesting_off, (char *)&ifnet, sizeof ifnet)) {
+ printf("???\n");
+ exit(1);
+ };
+ if (!first) {
+ printf("%10lu %5lu %10lu %10lu %5lu %10lu %5lu",
+ ifnet.if_ipackets - ip->ift_ip,
+ ifnet.if_ierrors - ip->ift_ie,
+ ifnet.if_ibytes - ip->ift_ib,
+ ifnet.if_opackets - ip->ift_op,
+ ifnet.if_oerrors - ip->ift_oe,
+ ifnet.if_obytes - ip->ift_ob,
+ 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_ib = ifnet.if_ibytes;
+ ip->ift_op = ifnet.if_opackets;
+ ip->ift_oe = ifnet.if_oerrors;
+ ip->ift_ob = ifnet.if_obytes;
+ ip->ift_co = ifnet.if_collisions;
+ ip->ift_dr = ifnet.if_snd.ifq_drops;
+ } else {
+ sum->ift_ip = 0;
+ sum->ift_ie = 0;
+ sum->ift_ib = 0;
+ sum->ift_op = 0;
+ sum->ift_oe = 0;
+ sum->ift_ob = 0;
+ sum->ift_co = 0;
+ sum->ift_dr = 0;
+ for (off = firstifnet, ip = iftot; off && ip < lastif; ip++) {
+ if (kread(off, (char *)&ifnet, sizeof ifnet)) {
+ off = 0;
+ continue;
+ }
+ sum->ift_ip += ifnet.if_ipackets;
+ sum->ift_ie += ifnet.if_ierrors;
+ sum->ift_ib += ifnet.if_ibytes;
+ sum->ift_op += ifnet.if_opackets;
+ sum->ift_oe += ifnet.if_oerrors;
+ sum->ift_ob += ifnet.if_obytes;
+ sum->ift_co += ifnet.if_collisions;
+ sum->ift_dr += ifnet.if_snd.ifq_drops;
+ off = (u_long) ifnet.if_link.tqe_next;
+ }
+ if (!first) {
+ printf("%10u %5u %10u %10u %5u %10u %5u",
+ sum->ift_ip - total->ift_ip,
+ sum->ift_ie - total->ift_ie,
+ sum->ift_ib - total->ift_ib,
+ sum->ift_op - total->ift_op,
+ sum->ift_oe - total->ift_oe,
+ sum->ift_ob - total->ift_ob,
+ sum->ift_co - total->ift_co);
+ if (dflag)
+ printf(" %5u", sum->ift_dr - total->ift_dr);
+ }
+ *total = *sum;
+ }
+ if (!first)
+ putchar('\n');
+ fflush(stdout);
+ oldmask = sigblock(sigmask(SIGALRM));
+ if (! signalled) {
+ sigpause(0);
+ }
+ sigsetmask(oldmask);
+ signalled = NO;
+ (void)alarm(interval);
+ line++;
+ first = 0;
+ if (line == 21)
+ goto banner;
+ else
+ 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..cc9290e
--- /dev/null
+++ b/usr.bin/netstat/inet.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 1983, 1988, 1993, 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 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.5 (Berkeley) 5/24/95";
+*/
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.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 *, int));
+
+/*
+ * 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 ", (int)inpcb.inp_ppcb);
+ else
+ printf("%8x ", (int)next);
+ printf("%-5.5s %6ld %6ld ", name, sockb.so_rcv.sb_cc,
+ sockb.so_snd.sb_cc);
+ if (nflag) {
+ inetprint(&inpcb.inp_laddr, (int)inpcb.inp_lport,
+ name, 1);
+ inetprint(&inpcb.inp_faddr, (int)inpcb.inp_fport,
+ name, 1);
+ } else if (inpcb.inp_flags & INP_ANONPORT) {
+ inetprint(&inpcb.inp_laddr, (int)inpcb.inp_lport,
+ name, 1);
+ inetprint(&inpcb.inp_faddr, (int)inpcb.inp_fport,
+ name, 0);
+ } else {
+ inetprint(&inpcb.inp_laddr, (int)inpcb.inp_lport,
+ name, 0);
+ inetprint(&inpcb.inp_faddr, (int)inpcb.inp_fport,
+ name, inpcb.inp_lport != inpcb.inp_fport);
+ }
+ 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_NEEDFIN)
+ /* Show T/TCP `hidden state' */
+ if (tcpcb.t_flags & (TF_NEEDSYN|TF_NEEDFIN))
+ putchar('*');
+#endif /* defined(TF_NEEDSYN) && defined(TF_NEEDFIN) */
+ }
+ }
+ 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%lu packet%s sent\n");
+ p2(tcps_sndpack,tcps_sndbyte,
+ "\t\t%lu data packet%s (%lu byte%s)\n");
+ p2(tcps_sndrexmitpack, tcps_sndrexmitbyte,
+ "\t\t%lu data packet%s (%lu byte%s) retransmitted\n");
+ p(tcps_mturesent, "\t\t%lu resend%s initiated by MTU discovery\n");
+ p2(tcps_sndacks, tcps_delack,
+ "\t\t%lu ack-only packet%s (%lu delayed)\n");
+ p(tcps_sndurg, "\t\t%lu URG only packet%s\n");
+ p(tcps_sndprobe, "\t\t%lu window probe packet%s\n");
+ p(tcps_sndwinup, "\t\t%lu window update packet%s\n");
+ p(tcps_sndctrl, "\t\t%lu control packet%s\n");
+ p(tcps_rcvtotal, "\t%lu packet%s received\n");
+ p2(tcps_rcvackpack, tcps_rcvackbyte, "\t\t%lu ack%s (for %lu byte%s)\n");
+ p(tcps_rcvdupack, "\t\t%lu duplicate ack%s\n");
+ p(tcps_rcvacktoomuch, "\t\t%lu ack%s for unsent data\n");
+ p2(tcps_rcvpack, tcps_rcvbyte,
+ "\t\t%lu packet%s (%lu byte%s) received in-sequence\n");
+ p2(tcps_rcvduppack, tcps_rcvdupbyte,
+ "\t\t%lu completely duplicate packet%s (%lu byte%s)\n");
+ p(tcps_pawsdrop, "\t\t%lu old duplicate packet%s\n");
+ p2(tcps_rcvpartduppack, tcps_rcvpartdupbyte,
+ "\t\t%lu packet%s with some dup. data (%lu byte%s duped)\n");
+ p2(tcps_rcvoopack, tcps_rcvoobyte,
+ "\t\t%lu out-of-order packet%s (%lu byte%s)\n");
+ p2(tcps_rcvpackafterwin, tcps_rcvbyteafterwin,
+ "\t\t%lu packet%s (%lu byte%s) of data after window\n");
+ p(tcps_rcvwinprobe, "\t\t%lu window probe%s\n");
+ p(tcps_rcvwinupd, "\t\t%lu window update packet%s\n");
+ p(tcps_rcvafterclose, "\t\t%lu packet%s received after close\n");
+ p(tcps_rcvbadsum, "\t\t%lu discarded for bad checksum%s\n");
+ p(tcps_rcvbadoff, "\t\t%lu discarded for bad header offset field%s\n");
+ p(tcps_rcvshort, "\t\t%lu discarded because packet too short\n");
+ p(tcps_connattempt, "\t%lu connection request%s\n");
+ p(tcps_accepts, "\t%lu connection accept%s\n");
+ p(tcps_badsyn, "\t%lu bad connection attempt%s\n");
+ p(tcps_listendrop, "\t%lu listen queue overflow%s\n");
+ p(tcps_connects, "\t%lu connection%s established (including accepts)\n");
+ p2(tcps_closed, tcps_drops,
+ "\t%lu connection%s closed (including %lu drop%s)\n");
+ p(tcps_cachedrtt, "\t\t%lu connection%s updated cached RTT on close\n");
+ p(tcps_cachedrttvar,
+ "\t\t%lu connection%s updated cached RTT variance on close\n");
+ p(tcps_cachedssthresh,
+ "\t\t%lu connection%s updated cached ssthresh on close\n");
+ p(tcps_conndrops, "\t%lu embryonic connection%s dropped\n");
+ p2(tcps_rttupdated, tcps_segstimed,
+ "\t%lu segment%s updated rtt (of %lu attempt%s)\n");
+ p(tcps_rexmttimeo, "\t%lu retransmit timeout%s\n");
+ p(tcps_timeoutdrop, "\t\t%lu connection%s dropped by rexmit timeout\n");
+ p(tcps_persisttimeo, "\t%lu persist timeout%s\n");
+ p(tcps_persistdrop, "\t\t%lu connection%s dropped by persist timeout\n");
+ p(tcps_keeptimeo, "\t%lu keepalive timeout%s\n");
+ p(tcps_keepprobe, "\t\t%lu keepalive probe%s sent\n");
+ p(tcps_keepdrops, "\t\t%lu connection%s dropped by keepalive\n");
+ p(tcps_predack, "\t%lu correct ACK header prediction%s\n");
+ p(tcps_preddat, "\t%lu 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%lu datagram%s received\n");
+ p(udps_hdrops, "\t%lu with incomplete header\n");
+ p(udps_badlen, "\t%lu with bad data length field\n");
+ p(udps_badsum, "\t%lu with bad checksum\n");
+ p(udps_noport, "\t%lu dropped due to no socket\n");
+ p(udps_noportbcast, "\t%lu broadcast/multicast datagram%s dropped due to no socket\n");
+ p(udps_fullsock, "\t%lu dropped due to full socket buffers\n");
+ p(udpps_pcbhashmiss, "\t%lu not for hashed pcb\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%lu delivered\n", delivered);
+ p(udps_opackets, "\t%lu 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%lu total packet%s received\n");
+ p(ips_badsum, "\t%lu bad header checksum%s\n");
+ p(ips_toosmall, "\t%lu with size smaller than minimum\n");
+ p(ips_tooshort, "\t%lu with data size < data length\n");
+ p(ips_badhlen, "\t%lu with header length < data size\n");
+ p(ips_badlen, "\t%lu with data length < header length\n");
+ p(ips_badoptions, "\t%lu with bad options\n");
+ p(ips_badvers, "\t%lu with incorrect version number\n");
+ p(ips_fragments, "\t%lu fragment%s received\n");
+ p(ips_fragdropped, "\t%lu fragment%s dropped (dup or out of space)\n");
+ p(ips_fragtimeout, "\t%lu fragment%s dropped after timeout\n");
+ p(ips_reassembled, "\t%lu packet%s reassembled ok\n");
+ p(ips_delivered, "\t%lu packet%s for this host\n");
+ p(ips_noproto, "\t%lu packet%s for unknown/unsupported protocol\n");
+ p(ips_forward, "\t%lu packet%s forwarded\n");
+ p(ips_cantforward, "\t%lu packet%s not forwardable\n");
+ p(ips_notmember,
+ "\t%lu packet%s received for unknown multicast group\n");
+ p(ips_redirectsent, "\t%lu redirect%s sent\n");
+ p(ips_localout, "\t%lu packet%s sent from this host\n");
+ p(ips_rawout, "\t%lu packet%s sent with fabricated ip header\n");
+ p(ips_odropped,
+ "\t%lu output packet%s dropped due to no bufs, etc.\n");
+ p(ips_noroute, "\t%lu output packet%s discarded due to no route\n");
+ p(ips_fragmented, "\t%lu output datagram%s fragmented\n");
+ p(ips_ofragments, "\t%lu fragment%s created\n");
+ p(ips_cantfrag, "\t%lu 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%lu call%s to icmp_error\n");
+ p(icps_oldicmp,
+ "\t%lu 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: %lu\n", icmpnames[i],
+ icmpstat.icps_outhist[i]);
+ }
+ p(icps_badcode, "\t%lu message%s with bad code fields\n");
+ p(icps_tooshort, "\t%lu message%s < minimum length\n");
+ p(icps_checksum, "\t%lu bad checksum%s\n");
+ p(icps_badlen, "\t%lu 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: %lu\n", icmpnames[i],
+ icmpstat.icps_inhist[i]);
+ }
+ p(icps_reflect, "\t%lu 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).
+ */
+void
+inetprint(in, port, proto,numeric)
+ register struct in_addr *in;
+ int port;
+ char *proto;
+ int numeric;
+{
+ struct servent *sp = 0;
+ char line[80], *cp;
+ int width;
+
+ sprintf(line, "%.*s.", (Aflag && !numeric) ? 12 : 16, inetname(in));
+ cp = index(line, '\0');
+ if (!numeric && port)
+ sp = getservbyport((int)port, proto);
+ if (sp || port == 0)
+ sprintf(cp, "%.15s", 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;
+
+ 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) {
+ cp = hp->h_name;
+ trimdomain(cp);
+ }
+ }
+ }
+ 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, "%lu.%lu.%lu.%lu", 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/ipx.c b/usr.bin/netstat/ipx.c
new file mode 100644
index 0000000..0e8d34f
--- /dev/null
+++ b/usr.bin/netstat/ipx.c
@@ -0,0 +1,364 @@
+/*
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id: ipx.c,v 1.7 1997/02/22 19:56:22 peter Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+
+#include <net/route.h>
+
+#include <netinet/tcp_fsm.h>
+
+#include <netipx/ipx.h>
+#include <netipx/ipx_pcb.h>
+#include <netipx/ipx_var.h>
+#ifdef IPXERRORMSGS
+#include <netipx/ipx_error.h>
+#endif
+#include <netipx/spx.h>
+#include <netipx/spx_timer.h>
+#include <netipx/spx_var.h>
+#define SANAMES
+#include <netipx/spx_debug.h>
+
+#include <nlist.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "netstat.h"
+
+struct ipxpcb ipxpcb;
+struct spxpcb spxpcb;
+struct socket sockb;
+
+static char *ipx_prpr __P((struct ipx_addr *));
+static void ipx_erputil __P((int, int));
+
+static int first = 1;
+
+/*
+ * Print a summary of connections related to a Network Systems
+ * protocol. For SPX, also give state of connection.
+ * Listening processes (aflag) are suppressed unless the
+ * -a (all) flag is specified.
+ */
+
+void
+ipxprotopr(off, name)
+ u_long off;
+ char *name;
+{
+ struct ipxpcb cb;
+ register struct ipxpcb *prev, *next;
+ int isspx;
+
+ if (off == 0)
+ return;
+ isspx = strcmp(name, "spx") == 0;
+ kread(off, (char *)&cb, sizeof (struct ipxpcb));
+ ipxpcb = cb;
+ prev = (struct ipxpcb *)off;
+ if (ipxpcb.ipxp_next == (struct ipxpcb *)off)
+ return;
+ for (;ipxpcb.ipxp_next != (struct ipxpcb *)off; prev = next) {
+ u_long ppcb;
+
+ next = ipxpcb.ipxp_next;
+ kread((u_long)next, (char *)&ipxpcb, sizeof (ipxpcb));
+ if (ipxpcb.ipxp_prev != prev) {
+ printf("???\n");
+ break;
+ }
+ if (!aflag && ipx_nullhost(ipxpcb.ipxp_faddr) ) {
+ continue;
+ }
+ kread((u_long)ipxpcb.ipxp_socket,
+ (char *)&sockb, sizeof (sockb));
+ ppcb = (u_long) ipxpcb.ipxp_pcb;
+ if (ppcb) {
+ if (isspx) {
+ kread(ppcb, (char *)&spxpcb, sizeof (spxpcb));
+ } else continue;
+ } else
+ if (isspx) continue;
+ if (first) {
+ printf("Active IPX 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("%8lx ", ppcb);
+ printf("%-5.5s %6ld %6ld ", name, sockb.so_rcv.sb_cc,
+ sockb.so_snd.sb_cc);
+ printf(Aflag?" %-18.18s":" %-22.22s", ipx_prpr(&ipxpcb.ipxp_laddr));
+ printf(Aflag?" %-18.18s":" %-22.22s", ipx_prpr(&ipxpcb.ipxp_faddr));
+ if (isspx) {
+ extern char *tcpstates[];
+ if (spxpcb.s_state >= TCP_NSTATES)
+ printf(" %d", spxpcb.s_state);
+ else
+ printf(" %s", tcpstates[spxpcb.s_state]);
+ }
+ putchar('\n');
+ prev = next;
+ }
+}
+
+#define ANY(x,y,z) (printf("\t%u %s%s%s\n",x,y,plural(x),z))
+
+/*
+ * Dump SPX statistics structure.
+ */
+void
+spx_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct spx_istat spx_istat;
+#define spxstat spx_istat.newstats
+
+ if (off == 0)
+ return;
+ kread(off, (char *)&spx_istat, sizeof (spx_istat));
+ printf("%s:\n", name);
+ ANY(spx_istat.nonucn, "connection", " dropped due to no new sockets ");
+ ANY(spx_istat.gonawy, "connection", " terminated due to our end dying");
+ ANY(spx_istat.nonucn, "connection",
+ " dropped due to inability to connect");
+ ANY(spx_istat.noconn, "connection",
+ " dropped due to inability to connect");
+ ANY(spx_istat.notme, "connection",
+ " incompleted due to mismatched id's");
+ ANY(spx_istat.wrncon, "connection", " dropped due to mismatched id's");
+ ANY(spx_istat.bdreas, "packet", " dropped out of sequence");
+ ANY(spx_istat.lstdup, "packet", " duplicating the highest packet");
+ ANY(spx_istat.notyet, "packet", " refused as exceeding allocation");
+ ANY(spxstat.spxs_connattempt, "connection", " initiated");
+ ANY(spxstat.spxs_accepts, "connection", " accepted");
+ ANY(spxstat.spxs_connects, "connection", " established");
+ ANY(spxstat.spxs_drops, "connection", " dropped");
+ ANY(spxstat.spxs_conndrops, "embryonic connection", " dropped");
+ ANY(spxstat.spxs_closed, "connection", " closed (includes drops)");
+ ANY(spxstat.spxs_segstimed, "packet", " where we tried to get rtt");
+ ANY(spxstat.spxs_rttupdated, "time", " we got rtt");
+ ANY(spxstat.spxs_delack, "delayed ack", " sent");
+ ANY(spxstat.spxs_timeoutdrop, "connection", " dropped in rxmt timeout");
+ ANY(spxstat.spxs_rexmttimeo, "retransmit timeout", "");
+ ANY(spxstat.spxs_persisttimeo, "persist timeout", "");
+ ANY(spxstat.spxs_keeptimeo, "keepalive timeout", "");
+ ANY(spxstat.spxs_keepprobe, "keepalive probe", " sent");
+ ANY(spxstat.spxs_keepdrops, "connection", " dropped in keepalive");
+ ANY(spxstat.spxs_sndtotal, "total packet", " sent");
+ ANY(spxstat.spxs_sndpack, "data packet", " sent");
+ ANY(spxstat.spxs_sndbyte, "data byte", " sent");
+ ANY(spxstat.spxs_sndrexmitpack, "data packet", " retransmitted");
+ ANY(spxstat.spxs_sndrexmitbyte, "data byte", " retransmitted");
+ ANY(spxstat.spxs_sndacks, "ack-only packet", " sent");
+ ANY(spxstat.spxs_sndprobe, "window probe", " sent");
+ ANY(spxstat.spxs_sndurg, "packet", " sent with URG only");
+ ANY(spxstat.spxs_sndwinup, "window update-only packet", " sent");
+ ANY(spxstat.spxs_sndctrl, "control (SYN|FIN|RST) packet", " sent");
+ ANY(spxstat.spxs_sndvoid, "request", " to send a non-existant packet");
+ ANY(spxstat.spxs_rcvtotal, "total packet", " received");
+ ANY(spxstat.spxs_rcvpack, "packet", " received in sequence");
+ ANY(spxstat.spxs_rcvbyte, "byte", " received in sequence");
+ ANY(spxstat.spxs_rcvbadsum, "packet", " received with ccksum errs");
+ ANY(spxstat.spxs_rcvbadoff, "packet", " received with bad offset");
+ ANY(spxstat.spxs_rcvshort, "packet", " received too short");
+ ANY(spxstat.spxs_rcvduppack, "duplicate-only packet", " received");
+ ANY(spxstat.spxs_rcvdupbyte, "duplicate-only byte", " received");
+ ANY(spxstat.spxs_rcvpartduppack, "packet", " with some duplicate data");
+ ANY(spxstat.spxs_rcvpartdupbyte, "dup. byte", " in part-dup. packet");
+ ANY(spxstat.spxs_rcvoopack, "out-of-order packet", " received");
+ ANY(spxstat.spxs_rcvoobyte, "out-of-order byte", " received");
+ ANY(spxstat.spxs_rcvpackafterwin, "packet", " with data after window");
+ ANY(spxstat.spxs_rcvbyteafterwin, "byte", " rcvd after window");
+ ANY(spxstat.spxs_rcvafterclose, "packet", " rcvd after 'close'");
+ ANY(spxstat.spxs_rcvwinprobe, "rcvd window probe packet", "");
+ ANY(spxstat.spxs_rcvdupack, "rcvd duplicate ack", "");
+ ANY(spxstat.spxs_rcvacktoomuch, "rcvd ack", " for unsent data");
+ ANY(spxstat.spxs_rcvackpack, "rcvd ack packet", "");
+ ANY(spxstat.spxs_rcvackbyte, "byte", " acked by rcvd acks");
+ ANY(spxstat.spxs_rcvwinupd, "rcvd window update packet", "");
+}
+
+#undef ANY
+#define ANY(x,y,z) (printf("\t%u %s%s%s\n",x,y,plural(x),z))
+
+/*
+ * Dump IPX statistics structure.
+ */
+void
+ipx_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct ipxstat ipxstat;
+
+ if (off == 0)
+ return;
+ kread(off, (char *)&ipxstat, sizeof (ipxstat));
+ printf("%s:\n", name);
+ ANY(ipxstat.ipxs_total, "total packet", " received");
+ ANY(ipxstat.ipxs_badsum, "packet", " with bad checksums");
+ ANY(ipxstat.ipxs_tooshort, "packet", " smaller than advertised");
+ ANY(ipxstat.ipxs_toosmall, "packet", " smaller than a header");
+ ANY(ipxstat.ipxs_forward, "packet", " forwarded");
+ ANY(ipxstat.ipxs_cantforward, "packet", " not forwardable");
+ ANY(ipxstat.ipxs_delivered, "packet", " for this host");
+ ANY(ipxstat.ipxs_localout, "packet", " sent from this host");
+ ANY(ipxstat.ipxs_odropped, "packet", " dropped due to no bufs, etc.");
+ ANY(ipxstat.ipxs_noroute, "packet", " discarded due to no route");
+ ANY(ipxstat.ipxs_mtutoosmall, "packet", " too big");
+}
+
+static struct {
+ u_short code;
+ char *name;
+ char *where;
+} ipx_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},
+};
+
+#ifdef IPXERRORMSGS
+/*
+ * Dump IPX Error statistics structure.
+ */
+/*ARGSUSED*/
+void
+ipxerr_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct ipx_errstat ipx_errstat;
+ register int j;
+ register int histoprint = 1;
+ int z;
+
+ if (off == 0)
+ return;
+ kread(off, (char *)&ipx_errstat, sizeof (ipx_errstat));
+ printf("IPX error statistics:\n");
+ ANY(ipx_errstat.ipx_es_error, "call", " to ipx_error");
+ ANY(ipx_errstat.ipx_es_oldshort, "error",
+ " ignored due to insufficient addressing");
+ ANY(ipx_errstat.ipx_es_oldipx_err, "error request",
+ " in response to error packets");
+ ANY(ipx_errstat.ipx_es_tooshort, "error packet",
+ " received incomplete");
+ ANY(ipx_errstat.ipx_es_badcode, "error packet",
+ " received of unknown type");
+ for(j = 0; j < IPX_ERR_MAX; j ++) {
+ z = ipx_errstat.ipx_es_outhist[j];
+ if (z && histoprint) {
+ printf("Output Error Histogram:\n");
+ histoprint = 0;
+ }
+ ipx_erputil(z, ipx_errstat.ipx_es_codes[j]);
+ }
+ histoprint = 1;
+ for(j = 0; j < IPX_ERR_MAX; j ++) {
+ z = ipx_errstat.ipx_es_inhist[j];
+ if (z && histoprint) {
+ printf("Input Error Histogram:\n");
+ histoprint = 0;
+ }
+ ipx_erputil(z, ipx_errstat.ipx_es_codes[j]);
+ }
+}
+
+static void
+ipx_erputil(z, c)
+ int z, c;
+{
+ int j;
+ char codebuf[30];
+ char *name, *where;
+
+ for(j = 0;; j ++) {
+ if ((name = ipx_errnames[j].name) == 0)
+ break;
+ if (ipx_errnames[j].code == c)
+ break;
+ }
+ if (name == 0) {
+ if (c > 01000)
+ where = "in transit";
+ else
+ where = "at destination";
+ sprintf(codebuf, "Unknown IPX error code 0%o", c);
+ name = codebuf;
+ } else
+ where = ipx_errnames[j].where;
+ ANY(z, name, where);
+}
+#endif /* IPXERRORMSGS */
+
+static struct sockaddr_ipx ssipx = {AF_IPX};
+
+static
+char *ipx_prpr(x)
+ struct ipx_addr *x;
+{
+ struct sockaddr_ipx *sipx = &ssipx;
+
+ sipx->sipx_addr = *x;
+ return(ipx_print((struct sockaddr *)sipx));
+}
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..08e15ee
--- /dev/null
+++ b/usr.bin/netstat/main.c
@@ -0,0 +1,611 @@
+/*
+ * 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
+ { "_localsw" },
+#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" },
+#define N_IPX 31
+ { "_ipxpcb"},
+#define N_IPXSTAT 32
+ { "_ipxstat"},
+#define N_SPXSTAT 33
+ { "_spx_istat"},
+#define N_DDPSTAT 34
+ { "_ddpstat"},
+#define N_DDPCB 35
+ { "_ddpcb"},
+#define N_DIVPCB 36
+ { "_divcb"},
+#define N_DIVSTAT 37
+ { "_divstat"},
+ { "" },
+};
+
+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" },
+ { N_DIVPCB, N_DIVSTAT, 1, protopr,
+ NULL, "divert" }, /* no stat structure yet */
+ { -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 atalkprotox[] = {
+ { N_DDPCB, N_DDPSTAT, 1, atalkprotopr,
+ ddp_stats, "ddp" },
+ { -1, -1, 0, 0,
+ 0, 0 }
+};
+
+struct protox ipxprotox[] = {
+ { N_IPX, N_IPXSTAT, 1, ipxprotopr,
+ ipx_stats, "ipx" },
+ { N_IPX, N_SPXSTAT, 1, ipxprotopr,
+ spx_stats, "spx" },
+ { -1, -1, 0, 0,
+ 0, 0 }
+};
+
+#ifdef NS
+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 }
+};
+#endif
+
+#ifdef ISO
+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 }
+};
+#endif
+
+struct protox *protoprotox[] = { protox, ipxprotox, atalkprotox,
+#ifdef NS
+ nsprotox,
+#endif
+#ifdef ISO
+ isoprotox,
+#endif
+ 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];
+
+ 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:")) != -1)
+ switch(ch) {
+ case 'A':
+ Aflag = 1;
+ break;
+ case 'a':
+ aflag = 1;
+ break;
+ case 'b':
+ bflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+#ifdef NS
+ if (strcmp(optarg, "ns") == 0)
+ af = AF_NS;
+ else
+#endif
+ if (strcmp(optarg, "ipx") == 0)
+ af = AF_IPX;
+ else if (strcmp(optarg, "inet") == 0)
+ af = AF_INET;
+ else if (strcmp(optarg, "unix") == 0)
+ af = AF_UNIX;
+ else if (strcmp(optarg, "atalk") == 0)
+ af = AF_APPLETALK;
+#ifdef ISO
+ else if (strcmp(optarg, "iso") == 0)
+ af = AF_ISO;
+#endif
+ 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);
+ }
+#if 0
+ /*
+ * Keep file descriptors open to avoid overhead
+ * of open/close on each call to get* routines.
+ */
+ sethostent(1);
+ setnetent(1);
+#else
+ /*
+ * This does not make sense any more with DNS being default over
+ * the files. Doing a setXXXXent(1) causes a tcp connection to be
+ * used for the queries, which is slower.
+ */
+#endif
+ 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_IPX || af == AF_UNSPEC)
+ for (tp = ipxprotox; tp->pr_name; tp++)
+ printproto(tp, tp->pr_name);
+ if (af == AF_APPLETALK || af == AF_UNSPEC)
+ for (tp = atalkprotox; tp->pr_name; tp++)
+ printproto(tp, tp->pr_name);
+#ifdef NS
+ if (af == AF_NS || af == AF_UNSPEC)
+ for (tp = nsprotox; tp->pr_name; tp++)
+ printproto(tp, tp->pr_name);
+#endif
+#ifdef ISO
+ if (af == AF_ISO || af == AF_UNSPEC)
+ for (tp = isoprotox; tp->pr_name; tp++)
+ printproto(tp, tp->pr_name);
+#endif
+ 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);
+}
+
+void
+trimdomain(cp)
+ char *cp;
+{
+ static char domain[MAXHOSTNAMELEN + 1];
+ static int first = 1;
+ char *s;
+
+ if (first) {
+ first = 0;
+ if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
+ (s = strchr(domain, '.')))
+ (void) strcpy(domain, s + 1);
+ else
+ domain[0] = 0;
+ }
+
+ if (domain[0]) {
+ while ((cp = strchr(cp, '.'))) {
+ if (!strcasecmp(cp + 1, domain)) {
+ *cp = 0; /* hit it */
+ break;
+ } else {
+ cp++;
+ }
+ }
+ }
+}
+
diff --git a/usr.bin/netstat/mbuf.c b/usr.bin/netstat/mbuf.c
new file mode 100644
index 0000000..cfefa5e
--- /dev/null
+++ b/usr.bin/netstat/mbuf.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[] = "@(#)mbuf.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/sysctl.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;
+ int name[3];
+ size_t mbstatlen;
+
+ name[0] = CTL_KERN;
+ name[1] = KERN_IPC;
+ name[2] = KIPC_MBSTAT;
+ mbstatlen = sizeof mbstat;
+
+ if (sysctl(name, 3, &mbstat, &mbstatlen, 0, 0) < 0) {
+ warn("sysctl: retrieving mbstat");
+ return;
+ }
+#undef MSIZE
+#define MSIZE (mbstat.m_msize)
+#undef MCLBYTES
+#define MCLBYTES (mbstat.m_mclbytes)
+
+ if (nmbtypes != 256) {
+ warnx("unexpected change to mbstat; check source");
+ return;
+ }
+
+ totmbufs = 0;
+ for (mp = mbtypes; mp->mt_name; mp++)
+ totmbufs += mbstat.m_mtypes[mp->mt_type];
+ printf("%u/%u mbufs in use:\n", totmbufs, mbstat.m_mbufs);
+ 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("%lu/%lu mbuf clusters in use\n",
+ mbstat.m_clusters - mbstat.m_clfree, mbstat.m_clusters);
+ totmem = mbstat.m_mbufs * MSIZE + mbstat.m_clusters * MCLBYTES;
+ totfree = mbstat.m_clfree * MCLBYTES +
+ MSIZE * (mbstat.m_mbufs - totmbufs);
+ printf("%u Kbytes allocated to network (%d%% in use)\n",
+ totmem / 1024, (totmem - totfree) * 100 / totmem);
+ printf("%lu requests for memory denied\n", mbstat.m_drops);
+ printf("%lu requests for memory delayed\n", mbstat.m_wait);
+ printf("%lu 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..e725d5d
--- /dev/null
+++ b/usr.bin/netstat/mroute.c
@@ -0,0 +1,234 @@
+/*
+ * 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.2 (Berkeley) 4/28/95
+ */
+
+/*
+ * Print DVMRP multicast routing structures and statistics.
+ *
+ * MROUTING 1.0
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/mbuf.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/igmp.h>
+#include <net/route.h>
+#include <netinet/ip_mroute.h>
+
+#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 int i;
+ register int banner_printed;
+ register int saved_nflag;
+ vifi_t maxvif = 0;
+
+ 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(" %10lu multicast forwarding cache lookup%s\n",
+ mrtstat.mrts_mfc_lookups, plural(mrtstat.mrts_mfc_lookups));
+ printf(" %10lu multicast forwarding cache miss%s\n",
+ mrtstat.mrts_mfc_misses, plurales(mrtstat.mrts_mfc_misses));
+ printf(" %10lu upcall%s to mrouted\n",
+ mrtstat.mrts_upcalls, plural(mrtstat.mrts_upcalls));
+ printf(" %10lu upcall queue overflow%s\n",
+ mrtstat.mrts_upq_ovflw, plural(mrtstat.mrts_upq_ovflw));
+ printf(" %10lu upcall%s dropped due to full socket buffer\n",
+ mrtstat.mrts_upq_sockfull, plural(mrtstat.mrts_upq_sockfull));
+ printf(" %10lu cache cleanup%s\n",
+ mrtstat.mrts_cache_cleanups, plural(mrtstat.mrts_cache_cleanups));
+ printf(" %10lu datagram%s with no route for origin\n",
+ mrtstat.mrts_no_route, plural(mrtstat.mrts_no_route));
+ printf(" %10lu datagram%s arrived with bad tunneling\n",
+ mrtstat.mrts_bad_tunnel, plural(mrtstat.mrts_bad_tunnel));
+ printf(" %10lu datagram%s could not be tunneled\n",
+ mrtstat.mrts_cant_tunnel, plural(mrtstat.mrts_cant_tunnel));
+ printf(" %10lu datagram%s arrived on wrong interface\n",
+ mrtstat.mrts_wrong_if, plural(mrtstat.mrts_wrong_if));
+ printf(" %10lu datagram%s selectively dropped\n",
+ mrtstat.mrts_drop_sel, plural(mrtstat.mrts_drop_sel));
+ printf(" %10lu datagram%s dropped due to queue overflow\n",
+ mrtstat.mrts_q_overflow, plural(mrtstat.mrts_q_overflow));
+ printf(" %10lu 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..f9afb7a
--- /dev/null
+++ b/usr.bin/netstat/netstat.1
@@ -0,0 +1,299 @@
+.\" 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 the interface display (option
+.Fl i
+, 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 ipx ,
+for
+.Dv AF_IPX ,
+.Ar atalk ,
+for
+.Dv AF_APPLETALK (ddp) ,
+.\".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)
+b RTF_BROADCAST The route represents a broadcast address
+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.
+By default, this display summarizes information for all interfaces.
+Information for a specific interface may be displayed with the
+.Fl I
+option.
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr ps 1 ,
+.Xr hosts 5 ,
+.Xr networks 5 ,
+.Xr protocols 5 ,
+.Xr services 5 ,
+.Xr iostat 8 ,
+.Xr trpt 8 ,
+.Xr trsp 8 ,
+.Xr vmstat 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..708094f
--- /dev/null
+++ b/usr.bin/netstat/netstat.h
@@ -0,0 +1,125 @@
+/*
+ * 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 trimdomain __P((char *));
+
+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 __P((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 *ipx_pnet __P((struct sockaddr *));
+char *ipx_phost __P((struct sockaddr *));
+char *ns_phost __P((struct sockaddr *));
+void upHex __P((char *));
+
+char *routename __P((u_long));
+char *netname __P((u_long, u_long));
+char *atalk_print __P((struct sockaddr *, int));
+char *atalk_print2 __P((struct sockaddr *, struct sockaddr *, int));
+char *ipx_print __P((struct sockaddr *));
+char *ns_print __P((struct sockaddr *));
+void routepr __P((u_long));
+
+void ipxprotopr __P((u_long, char *));
+void spx_stats __P((u_long, char *));
+void ipx_stats __P((u_long, char *));
+void ipxerr_stats __P((u_long, char *));
+
+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 atalkprotopr __P((u_long, char *));
+void ddp_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..e920aaa
--- /dev/null
+++ b/usr.bin/netstat/route.c
@@ -0,0 +1,861 @@
+/*
+ * 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.6 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$Id: route.c,v 1.26 1997/05/10 10:03:43 jhay Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netipx/ipx.h>
+#include <netatalk/at.h>
+
+#ifdef NS
+#include <netns/ns.h>
+#endif
+
+#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_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' },
+ { RTF_BLACKHOLE,'B' },
+ { RTF_BROADCAST,'b' },
+ { 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 *, 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_IPX:
+ afname = "IPX";
+ break;
+#ifdef NS
+ case AF_NS:
+ afname = "XNS";
+ break;
+#endif
+ case AF_ISO:
+ afname = "ISO";
+ break;
+ case AF_APPLETALK:
+ afname = "AppleTalk";
+ 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 18 /* 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 ", (int)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),
+ NULL, 0, 44);
+ putchar('\n');
+ }
+ if ((rn = rnode.rn_dupedkey))
+ goto again;
+ } else {
+ if (Aflag && do_rtent) {
+ printf("%-8.8x ", (int)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),
+ NULL, 0, -1);
+ } else if (rm == 0)
+ return;
+ } else {
+ sprintf(nbuf, "(%d)", rnode.rn_b);
+ printf("%6.6s %8.8x : %8.8x", nbuf, (int)rnode.rn_l, (int)rnode.rn_r);
+ }
+ while (rm) {
+ kget(rm, rmask);
+ sprintf(nbuf, " %d refs, ", rmask.rm_refs);
+ printf(" mk = %8.8x {(%d),%s",
+ (int)rm, -1 - rmask.rm_b, rmask.rm_refs ? nbuf : " ");
+ if (rmask.rm_flags & RNF_NORMAL) {
+ struct radix_node rnode_aux;
+ printf(" <normal>, ");
+ kget(rmask.rm_leaf, rnode_aux);
+ p_sockaddr(kgetsa((struct sockaddr *)rnode_aux.rn_mask),
+ NULL, 0, -1);
+ } else
+ p_sockaddr(kgetsa((struct sockaddr *)rmask.rm_mask),
+ NULL, 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, NULL, 0, 36);
+ else {
+ p_sockaddr(sa, NULL, 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, NULL, 0, 18);
+ }
+ p_flags(rtm->rtm_flags & interesting, "%-6.6s ");
+ putchar('\n');
+}
+
+static void
+p_sockaddr(sa, mask, flags, width)
+ struct sockaddr *sa, *mask;
+ 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;
+
+ if (sin->sin_addr.s_addr == INADDR_ANY)
+ cp = "default";
+ else if (flags & RTF_HOST)
+ cp = routename(sin->sin_addr.s_addr);
+ else if (mask)
+ cp = netname(sin->sin_addr.s_addr,
+ ntohl(((struct sockaddr_in *)mask)
+ ->sin_addr.s_addr));
+ else
+ cp = netname(sin->sin_addr.s_addr, 0L);
+ break;
+ }
+
+ case AF_IPX:
+ {
+ struct ipx_addr work = ((struct sockaddr_ipx *)sa)->sipx_addr;
+ if (ipx_nullnet(satoipx_addr(work)))
+ cp = "default";
+ else
+ cp = ipx_print(sa);
+ break;
+ }
+ case AF_APPLETALK:
+ {
+ if (!(flags & RTF_HOST) && mask)
+ cp = atalk_print2(sa,mask,9);
+ else
+ cp = atalk_print(sa,11);
+ break;
+ }
+#ifdef NS
+ case AF_NS:
+ cp = ns_print(sa);
+ break;
+#endif
+
+ 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];
+ struct sockaddr *sa;
+ struct sockaddr addr, mask;
+
+ /*
+ * Don't print protocol-cloned routes unless -a.
+ */
+ if(rt->rt_parent && !aflag)
+ return;
+
+ if (!(sa = kgetsa(rt_key(rt))))
+ bzero(&addr, sizeof addr);
+ else
+ addr = *sa;
+ if (!rt_mask(rt) || !(sa = kgetsa(rt_mask(rt))))
+ bzero(&mask, sizeof mask);
+ else
+ mask = *sa;
+ p_sockaddr(&addr, &mask, rt->rt_flags, WID_DST);
+ p_sockaddr(kgetsa(rt->rt_gateway), NULL, RTF_HOST, WID_GW);
+ p_flags(rt->rt_flags, "%-6.6s ");
+ printf("%6d %8ld ", 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,
+ (int)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;
+
+ cp = 0;
+ if (!nflag) {
+ hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
+ AF_INET);
+ if (hp) {
+ cp = hp->h_name;
+ trimdomain(cp);
+ }
+ }
+ if (cp)
+ strncpy(line, cp, sizeof(line) - 1);
+ else {
+#define C(x) ((x) & 0xff)
+ in = ntohl(in);
+ sprintf(line, "%lu.%lu.%lu.%lu",
+ C(in >> 24), C(in >> 16), C(in >> 8), C(in));
+ }
+ return (line);
+}
+
+static u_long
+forgemask(a)
+ u_long a;
+{
+ u_long m;
+
+ if (IN_CLASSA(a))
+ m = IN_CLASSA_NET;
+ else if (IN_CLASSB(a))
+ m = IN_CLASSB_NET;
+ else
+ m = IN_CLASSC_NET;
+ return (m);
+}
+
+static void
+domask(dst, addr, mask)
+ char *dst;
+ u_long addr, mask;
+{
+ register int b, i;
+
+ if (!mask || (forgemask(addr) == mask)) {
+ *dst = '\0';
+ return;
+ }
+ i = 0;
+ for (b = 0; b < 32; b++)
+ if (mask & (1 << b)) {
+ register int bb;
+
+ i = b;
+ for (bb = b+1; bb < 32; bb++)
+ if (!(mask & (1 << bb))) {
+ i = -1; /* noncontig */
+ break;
+ }
+ break;
+ }
+ if (i == -1)
+ sprintf(dst, "&0x%lx", mask);
+ else
+ sprintf(dst, "/%d", 32-i);
+}
+
+/*
+ * 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, omask, dmask;
+ register u_long i;
+ int subnetshift;
+
+ i = ntohl(in);
+ omask = mask;
+ if (!nflag && i) {
+ dmask = forgemask(i);
+ net = i & dmask;
+ if (!(np = getnetbyaddr(i, AF_INET)) && net != i)
+ np = getnetbyaddr(net, AF_INET);
+ if (np) {
+ cp = np->n_name;
+ trimdomain(cp);
+ }
+ }
+ if (cp)
+ strncpy(line, cp, sizeof(line) - 1);
+ else if ((i & 0xffffff) == 0)
+ sprintf(line, "%lu", C(i >> 24));
+ else if ((i & 0xffff) == 0)
+ sprintf(line, "%lu.%lu", C(i >> 24) , C(i >> 16));
+ else if ((i & 0xff) == 0)
+ sprintf(line, "%lu.%lu.%lu", C(i >> 24), C(i >> 16), C(i >> 8));
+ else
+ sprintf(line, "%lu.%lu.%lu.%lu", C(i >> 24),
+ C(i >> 16), C(i >> 8), C(i));
+ domask(line+strlen(line), i, omask);
+ 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));
+}
+
+char *
+ipx_print(sa)
+ register struct sockaddr *sa;
+{
+ u_short port;
+ struct netent *np = 0;
+ struct hostent *hp = 0;
+ struct servent *sp = 0;
+ char *net = "", *host = "";
+ register char *p; register u_char *q;
+ struct ipx_addr work = ((struct sockaddr_ipx *)sa)->sipx_addr;
+static char mybuf[50];
+ char cport[10], chost[15], cnet[15];
+
+ port = ntohs(work.x_port);
+
+ if (ipx_nullnet(work) && ipx_nullhost(work)) {
+
+ if (port) {
+ if (sp) sprintf(mybuf, "*.%s", sp->s_name);
+ else sprintf(mybuf, "*.%x", port);
+ } else
+ sprintf(mybuf, "*.*");
+
+ return (mybuf);
+ }
+
+ if (ipx_wildnet(work))
+ net = "any";
+ else if (ipx_nullnet(work))
+ net = "*";
+ else {
+ q = work.x_net.c_net;
+ sprintf(cnet, "%02x%02x%02x%02x",
+ q[0], q[1], q[2], q[3]);
+ for (p = cnet; *p == '0' && p < cnet + 8; p++)
+ continue;
+ net = p;
+ }
+
+ if (ipx_wildhost(work))
+ host = "any";
+ else if (ipx_nullhost(work))
+ host = "*";
+ else {
+ q = work.x_host.c_host;
+ sprintf(chost, "%02x%02x%02x%02x%02x%02x",
+ 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) {
+ if (strcmp(host, "*") == 0) host = "";
+ if (sp) sprintf(cport, "%s%s", *host ? "." : "", sp->s_name);
+ else sprintf(cport, "%s%x", *host ? "." : "", port);
+ } else
+ *cport = 0;
+
+ sprintf(mybuf,"%s.%s%s", net, host, cport);
+ return(mybuf);
+}
+
+char *
+ipx_phost(sa)
+ struct sockaddr *sa;
+{
+ register struct sockaddr_ipx *sipx = (struct sockaddr_ipx *)sa;
+ struct sockaddr_ipx work;
+static union ipx_net ipx_zeronet;
+ char *p;
+ struct ipx_addr in;
+ struct hostent *hp;
+
+ work = *sipx;
+ in = work.sipx_addr;
+
+ work.sipx_addr.x_port = 0;
+ work.sipx_addr.x_net = ipx_zeronet;
+ p = ipx_print((struct sockaddr *)&work);
+ if (strncmp("*.", p, 2) == 0) p += 2;
+
+ return(p);
+}
+
+#ifdef NS
+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);
+}
+#endif
+
+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..0309d1c
--- /dev/null
+++ b/usr.bin/netstat/unix.c
@@ -0,0 +1,135 @@
+/*-
+ * 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/queue.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 = NULL;
+ 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 %6ld %6ld %8x %8x %8x %8x",
+ (int)soaddr, socktype[so->so_type], so->so_rcv.sb_cc, so->so_snd.sb_cc,
+ (int)unp->unp_vnode, (int)unp->unp_conn,
+ (int)unp->unp_refs, (int)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/newkey/Makefile b/usr.bin/newkey/Makefile
new file mode 100644
index 0000000..bd62ecb
--- /dev/null
+++ b/usr.bin/newkey/Makefile
@@ -0,0 +1,13 @@
+# $Id$
+
+PROG= newkey
+SRCS= newkey.c update.c generic.c
+
+MAN8= newkey.8
+
+# Later
+#CFLAGS+= -DYP
+
+LDADD+= -lrpcsvc -lmp -lgmp
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/newkey/generic.c b/usr.bin/newkey/generic.c
new file mode 100644
index 0000000..4d15185
--- /dev/null
+++ b/usr.bin/newkey/generic.c
@@ -0,0 +1,135 @@
+/*
+ * 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 or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * 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
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)generic.c 1.2 91/03/11 Copyr 1986 Sun Micro";
+#endif
+
+/*
+ * Copyright (C) 1986, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <sys/file.h>
+#include <mp.h>
+#include <rpc/key_prot.h>
+
+static int adjust __P(( char[], char * ));
+/*
+ * Generate a seed
+ */
+static
+getseed(seed, seedsize, pass)
+ char *seed;
+ int seedsize;
+ unsigned char *pass;
+{
+ int i;
+ int rseed;
+ struct timeval tv;
+
+ (void)gettimeofday(&tv, (struct timezone *)NULL);
+ rseed = tv.tv_sec + tv.tv_usec;
+ for (i = 0; i < 8; i++) {
+ rseed ^= (rseed << 8) | pass[i];
+ }
+ srand(rseed);
+
+ for (i = 0; i < seedsize; i++) {
+ seed[i] = (rand() & 0xff) ^ pass[i % 8];
+ }
+}
+
+/*
+ * Generate a random public/secret key pair
+ */
+genkeys(public, secret, pass)
+ char *public;
+ char *secret;
+ char *pass;
+{
+ int i;
+
+# define BASEBITS (8*sizeof (short) - 1)
+# define BASE (1 << BASEBITS)
+
+ MINT *pk = itom(0);
+ MINT *sk = itom(0);
+ MINT *tmp;
+ MINT *base = itom(BASE);
+ MINT *root = itom(PROOT);
+ MINT *modulus = xtom(HEXMODULUS);
+ short r;
+ unsigned short seed[KEYSIZE/BASEBITS + 1];
+ char *xkey;
+
+ getseed((char *)seed, sizeof (seed), (u_char *)pass);
+ for (i = 0; i < KEYSIZE/BASEBITS + 1; i++) {
+ r = seed[i] % BASE;
+ tmp = itom(r);
+ mult(sk, base, sk);
+ madd(sk, tmp, sk);
+ mfree(tmp);
+ }
+ tmp = itom(0);
+ mdiv(sk, modulus, tmp, sk);
+ mfree(tmp);
+ pow(root, sk, modulus, pk);
+ xkey = mtox(sk);
+ adjust(secret, xkey);
+ xkey = mtox(pk);
+ adjust(public, xkey);
+ mfree(sk);
+ mfree(base);
+ mfree(pk);
+ mfree(root);
+ mfree(modulus);
+}
+
+/*
+ * Adjust the input key so that it is 0-filled on the left
+ */
+static
+adjust(keyout, keyin)
+ char keyout[HEXKEYBYTES+1];
+ char *keyin;
+{
+ char *p;
+ char *s;
+
+ for (p = keyin; *p; p++)
+ ;
+ for (s = keyout + HEXKEYBYTES; p >= keyin; p--, s--) {
+ *s = *p;
+ }
+ while (s >= keyout) {
+ *s-- = '0';
+ }
+}
diff --git a/usr.bin/newkey/newkey.8 b/usr.bin/newkey/newkey.8
new file mode 100644
index 0000000..8029751
--- /dev/null
+++ b/usr.bin/newkey/newkey.8
@@ -0,0 +1,57 @@
+.\" @(#)newkey.8 1.3 91/03/11 TIRPC 1.0; from 1.12 90/02/03 SMI;
+.TH NEWKEY 8 "12 October 1987"
+.SH NAME
+newkey \- create a new key in the publickey database
+.SH SYNOPSIS
+.B "newkey \-h"
+.I hostname
+.br
+.B "newkey \-u"
+.I username
+.SH DESCRIPTION
+.IX "newkey command" "" "\fLnewkey\fP command"
+.LP
+.B newkey
+is normally run by the network administrator on the
+Network Interface Service
+(\s-1NIS\s0)
+master machine in order to establish public keys for
+users and super-users on the network.
+These keys are needed for using secure
+.SM RPC
+or secure
+.SM NFS\s0.
+.LP
+.B newkey
+will prompt for the login password of the given username and then
+create a new public/secret key pair in
+.B /etc/publickey
+encrypted with the login password of the given user.
+.LP
+Use of this program is
+not required: users may create their own keys using
+.BR chkey (1).
+.SH OPTIONS
+.TP 12
+.BI \-h " hostname"
+Create a new public key for the super-user at the given hostname.
+Prompts for the root password of the given hostname.
+.TP
+.BI \-u " username"
+Create a new public key for the given username.
+Prompts for the
+.SM NIS
+password of the given username.
+.SH "SEE ALSO"
+.BR chkey (1),
+.BR keylogin (1),
+.BR publickey (5),
+.BR keyserv (8C)
+.SH NOTES
+.LP
+The Network Information Service
+(\s-1NIS\s0)
+was formerly known as Sun Yellow Pages
+(\s-1YP\s0).
+The functionality of the two remains the same;
+only the name has changed.
diff --git a/usr.bin/newkey/newkey.c b/usr.bin/newkey/newkey.c
new file mode 100644
index 0000000..489324d
--- /dev/null
+++ b/usr.bin/newkey/newkey.c
@@ -0,0 +1,235 @@
+/*
+ * 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 or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * 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
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)newkey.c 1.8 91/03/11 Copyr 1986 Sun Micro";
+#endif
+
+/*
+ * Copyright (C) 1986, Sun Microsystems, Inc.
+ */
+
+/*
+ * Administrative tool to add a new user to the publickey database
+ */
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#ifdef YP
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#endif /* YP */
+#include <pwd.h>
+#include <string.h>
+#include <sys/resource.h>
+
+#ifdef YP
+#define MAXMAPNAMELEN 256
+#else
+#define YPOP_CHANGE 1 /* change, do not add */
+#define YPOP_INSERT 2 /* add, do not change */
+#define YPOP_DELETE 3 /* delete this entry */
+#define YPOP_STORE 4 /* add, or change */
+#define ERR_ACCESS 1
+#define ERR_MALLOC 2
+#define ERR_READ 3
+#define ERR_WRITE 4
+#define ERR_DBASE 5
+#define ERR_KEY 6
+#endif
+
+extern char *getpass();
+extern char *malloc();
+
+#ifdef YP
+static char *basename();
+static char SHELL[] = "/bin/sh";
+static char YPDBPATH[]="/var/yp";
+static char PKMAP[] = "publickey.byname";
+static char UPDATEFILE[] = "updaters";
+#else
+static char PKFILE[] = "/etc/publickey";
+static char *err_string();
+#endif /* YP */
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char name[MAXNETNAMELEN + 1];
+ char public[HEXKEYBYTES + 1];
+ char secret[HEXKEYBYTES + 1];
+ char crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE + 1];
+ char crypt2[HEXKEYBYTES + KEYCHECKSUMSIZE + 1];
+ int status;
+ char *pass;
+ struct passwd *pw;
+#ifdef undef
+ struct hostent *h;
+#endif
+
+ if (argc != 3 || !(strcmp(argv[1], "-u") == 0 ||
+ strcmp(argv[1], "-h") == 0)) {
+ (void)fprintf(stderr, "usage: %s [-u username]\n",
+ argv[0]);
+ (void)fprintf(stderr, "usage: %s [-h hostname]\n",
+ argv[0]);
+ exit(1);
+ }
+ if (geteuid() != 0) {
+ (void)fprintf(stderr, "must be superuser to run %s\n", argv[0]);
+ exit(1);
+ }
+
+#ifdef YP
+ if (chdir(YPDBPATH) < 0) {
+ (void)fprintf(stderr, "cannot chdir to ");
+ perror(YPDBPATH);
+ }
+#endif /* YP */
+ if (strcmp(argv[1], "-u") == 0) {
+ pw = getpwnam(argv[2]);
+ if (pw == NULL) {
+ (void)fprintf(stderr, "unknown user: %s\n", argv[2]);
+ exit(1);
+ }
+ (void)user2netname(name, (int)pw->pw_uid, (char *)NULL);
+ } else {
+#ifdef undef
+ h = gethostbyname(argv[2]);
+ if (h == NULL) {
+ (void)fprintf(stderr, "unknown host: %s\n", argv[1]);
+ exit(1);
+ }
+ (void)host2netname(name, h->h_name, (char *)NULL);
+#else
+ (void)host2netname(name, argv[2], (char *)NULL);
+#endif
+ }
+
+ (void)printf("Adding new key for %s.\n", name);
+ pass = getpass("New password:");
+ genkeys(public, secret, pass);
+
+ memcpy(crypt1, secret, HEXKEYBYTES);
+ memcpy(crypt1 + HEXKEYBYTES, secret, KEYCHECKSUMSIZE);
+ crypt1[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0;
+ xencrypt(crypt1, pass);
+
+ memcpy(crypt2, crypt1, HEXKEYBYTES + KEYCHECKSUMSIZE + 1);
+ xdecrypt(crypt2, getpass("Retype password:"));
+ if (memcmp(crypt2, crypt2 + HEXKEYBYTES, KEYCHECKSUMSIZE) != 0 ||
+ memcmp(crypt2, secret, HEXKEYBYTES) != 0) {
+ (void)fprintf(stderr, "Password incorrect.\n");
+ exit(1);
+ }
+
+#ifdef YP
+ (void)printf("Please wait for the database to get updated...\n");
+#endif
+ if (status = setpublicmap(name, public, crypt1)) {
+#ifdef YP
+ (void)fprintf(stderr,
+ "%s: unable to update NIS database (%u): %s\n",
+ argv[0], status, yperr_string(status));
+#else
+ (void)fprintf(stderr,
+ "%s: unable to update publickey database (%u): %s\n",
+ argv[0], status, err_string(status));
+#endif
+ exit(1);
+ }
+ (void)printf("Your new key has been successfully stored away.\n");
+ exit(0);
+ /* NOTREACHED */
+}
+
+/*
+ * Set the entry in the public key file
+ */
+setpublicmap(name, public, secret)
+ char *name;
+ char *public;
+ char *secret;
+{
+ char pkent[1024];
+
+ (void)sprintf(pkent, "%s:%s", public, secret);
+#ifdef YP
+ return (mapupdate(name, PKMAP, YPOP_STORE,
+ strlen(name), name, strlen(pkent), pkent));
+#else
+ return (localupdate(name, PKFILE, YPOP_STORE,
+ strlen(name), name, strlen(pkent), pkent));
+#endif
+ }
+
+#ifndef YP
+ /*
+ * This returns a pointer to an error message string appropriate
+ * to an input error code. An input value of zero will return
+ * a success message.
+ */
+static char *
+err_string(code)
+ int code;
+{
+ char *pmesg;
+
+ switch (code) {
+ case 0:
+ pmesg = "update operation succeeded";
+ break;
+ case ERR_KEY:
+ pmesg = "no such key in file";
+ break;
+ case ERR_READ:
+ pmesg = "cannot read the database";
+ break;
+ case ERR_WRITE:
+ pmesg = "cannot write to the database";
+ break;
+ case ERR_DBASE:
+ pmesg = "cannot update database";
+ break;
+ case ERR_ACCESS:
+ pmesg = "permission denied";
+ break;
+ case ERR_MALLOC:
+ pmesg = "malloc failed";
+ break;
+ default:
+ pmesg = "unknown error";
+ break;
+ }
+ return (pmesg);
+}
+#endif
diff --git a/usr.bin/newkey/update.c b/usr.bin/newkey/update.c
new file mode 100644
index 0000000..e6bc9a6
--- /dev/null
+++ b/usr.bin/newkey/update.c
@@ -0,0 +1,356 @@
+/*
+ * 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 or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * 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[] = "@(#)update.c 1.2 91/03/11 Copyr 1986 Sun Micro";
+#endif
+
+/*
+ * Copyright (C) 1986, 1989, Sun Microsystems, Inc.
+ */
+
+/*
+ * Administrative tool to add a new user to the publickey database
+ */
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#ifdef YP
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#endif /* YP */
+#include <pwd.h>
+#include <string.h>
+#include <sys/resource.h>
+
+#ifdef YP
+#define MAXMAPNAMELEN 256
+#else
+#define YPOP_CHANGE 1 /* change, do not add */
+#define YPOP_INSERT 2 /* add, do not change */
+#define YPOP_DELETE 3 /* delete this entry */
+#define YPOP_STORE 4 /* add, or change */
+#endif
+
+extern char *getpass();
+extern char *malloc();
+
+#ifdef YP
+static char *basename();
+static char SHELL[] = "/bin/sh";
+static char YPDBPATH[]="/var/yp"; /* This is defined but not used! */
+static char PKMAP[] = "publickey.byname";
+static char UPDATEFILE[] = "updaters";
+#else
+static char PKFILE[] = "/etc/publickey";
+#endif /* YP */
+
+#ifdef YP
+static int _openchild __P(( char *, FILE **, FILE ** ));
+
+/*
+ * Determine if requester is allowed to update the given map,
+ * and update it if so. Returns the yp status, which is zero
+ * if there is no access violation.
+ */
+mapupdate(requester, mapname, op, keylen, key, datalen, data)
+ char *requester;
+ char *mapname;
+ u_int op;
+ u_int keylen;
+ char *key;
+ u_int datalen;
+ char *data;
+{
+ char updater[MAXMAPNAMELEN + 40];
+ FILE *childargs;
+ FILE *childrslt;
+#ifdef WEXITSTATUS
+ int status;
+#else
+ union wait status;
+#endif
+ pid_t pid;
+ u_int yperrno;
+
+
+#ifdef DEBUG
+ printf("%s %s\n", key, data);
+#endif
+ (void)sprintf(updater, "make -s -f %s/%s %s", YPDBPATH, /* !!! */
+ UPDATEFILE, mapname);
+ pid = _openchild(updater, &childargs, &childrslt);
+ if (pid < 0) {
+ return (YPERR_YPERR);
+ }
+
+ /*
+ * Write to child
+ */
+ (void)fprintf(childargs, "%s\n", requester);
+ (void)fprintf(childargs, "%u\n", op);
+ (void)fprintf(childargs, "%u\n", keylen);
+ (void)fwrite(key, (int)keylen, 1, childargs);
+ (void)fprintf(childargs, "\n");
+ (void)fprintf(childargs, "%u\n", datalen);
+ (void)fwrite(data, (int)datalen, 1, childargs);
+ (void)fprintf(childargs, "\n");
+ (void)fclose(childargs);
+
+ /*
+ * Read from child
+ */
+ (void)fscanf(childrslt, "%d", &yperrno);
+ (void)fclose(childrslt);
+
+ (void)wait(&status);
+#ifdef WEXITSTATUS
+ if (WEXITSTATUS(status) != 0) {
+#else
+ if (status.w_retcode != 0) {
+#endif
+ return (YPERR_YPERR);
+ }
+ return (yperrno);
+}
+
+/*
+ * returns pid, or -1 for failure
+ */
+static
+_openchild(command, fto, ffrom)
+ char *command;
+ FILE **fto;
+ FILE **ffrom;
+{
+ int i;
+ pid_t pid;
+ int pdto[2];
+ int pdfrom[2];
+ char *com;
+ struct rlimit rl;
+
+ if (pipe(pdto) < 0) {
+ goto error1;
+ }
+ if (pipe(pdfrom) < 0) {
+ goto error2;
+ }
+#ifdef VFORK
+ switch (pid = vfork()) {
+#else
+ switch (pid = fork()) {
+#endif
+ case -1:
+ goto error3;
+
+ case 0:
+ /*
+ * child: read from pdto[0], write into pdfrom[1]
+ */
+ (void)close(0);
+ (void)dup(pdto[0]);
+ (void)close(1);
+ (void)dup(pdfrom[1]);
+ getrlimit(RLIMIT_NOFILE, &rl);
+ for (i = rl.rlim_max - 1; i >= 3; i--) {
+ (void) close(i);
+ }
+ com = malloc((unsigned) strlen(command) + 6);
+ if (com == NULL) {
+ _exit(~0);
+ }
+ (void)sprintf(com, "exec %s", command);
+ execl(SHELL, basename(SHELL), "-c", com, NULL);
+ _exit(~0);
+
+ default:
+ /*
+ * parent: write into pdto[1], read from pdfrom[0]
+ */
+ *fto = fdopen(pdto[1], "w");
+ (void)close(pdto[0]);
+ *ffrom = fdopen(pdfrom[0], "r");
+ (void)close(pdfrom[1]);
+ break;
+ }
+ return (pid);
+
+ /*
+ * error cleanup and return
+ */
+error3:
+ (void)close(pdfrom[0]);
+ (void)close(pdfrom[1]);
+error2:
+ (void)close(pdto[0]);
+ (void)close(pdto[1]);
+error1:
+ return (-1);
+}
+
+static char *
+basename(path)
+ char *path;
+{
+ char *p;
+
+ p = strrchr(path, '/');
+ if (p == NULL) {
+ return (path);
+ } else {
+ return (p + 1);
+ }
+}
+
+#else /* YP */
+
+#define ERR_ACCESS 1
+#define ERR_MALLOC 2
+#define ERR_READ 3
+#define ERR_WRITE 4
+#define ERR_DBASE 5
+#define ERR_KEY 6
+extern char *malloc();
+
+static int match __P(( char * , char * ));
+
+/*
+ * Determine if requester is allowed to update the given map,
+ * and update it if so. Returns the status, which is zero
+ * if there is no access violation. This function updates
+ * the local file and then shuts up.
+ */
+localupdate(name, filename, op, keylen, key, datalen, data)
+ char *name; /* Name of the requestor */
+ char *filename;
+ u_int op;
+ u_int keylen; /* Not used */
+ char *key;
+ u_int datalen; /* Not used */
+ char *data;
+{
+ char line[256];
+ FILE *rf;
+ FILE *wf;
+ char *tmpname;
+ int err;
+
+ /*
+ * Check permission
+ */
+ if (strcmp(name, key) != 0) {
+ return (ERR_ACCESS);
+ }
+ if (strcmp(name, "nobody") == 0) {
+ /*
+ * Can't change "nobody"s key.
+ */
+ return (ERR_ACCESS);
+ }
+
+ /*
+ * Open files
+ */
+ tmpname = malloc(strlen(filename) + 4);
+ if (tmpname == NULL) {
+ return (ERR_MALLOC);
+ }
+ sprintf(tmpname, "%s.tmp", filename);
+ rf = fopen(filename, "r");
+ if (rf == NULL) {
+ return (ERR_READ);
+ }
+ wf = fopen(tmpname, "w");
+ if (wf == NULL) {
+ return (ERR_WRITE);
+ }
+ err = -1;
+ while (fgets(line, sizeof (line), rf)) {
+ if (err < 0 && match(line, name)) {
+ switch (op) {
+ case YPOP_INSERT:
+ err = ERR_KEY;
+ break;
+ case YPOP_STORE:
+ case YPOP_CHANGE:
+ fprintf(wf, "%s %s\n", key, data);
+ err = 0;
+ break;
+ case YPOP_DELETE:
+ /* do nothing */
+ err = 0;
+ break;
+ }
+ } else {
+ fputs(line, wf);
+ }
+ }
+ if (err < 0) {
+ switch (op) {
+ case YPOP_CHANGE:
+ case YPOP_DELETE:
+ err = ERR_KEY;
+ break;
+ case YPOP_INSERT:
+ case YPOP_STORE:
+ err = 0;
+ fprintf(wf, "%s %s\n", key, data);
+ break;
+ }
+ }
+ fclose(wf);
+ fclose(rf);
+ if (err == 0) {
+ if (rename(tmpname, filename) < 0) {
+ return (ERR_DBASE);
+ }
+ } else {
+ if (unlink(tmpname) < 0) {
+ return (ERR_DBASE);
+ }
+ }
+ return (err);
+}
+
+static
+match(line, name)
+ char *line;
+ char *name;
+{
+ int len;
+
+ len = strlen(name);
+ return (strncmp(line, name, len) == 0 &&
+ (line[len] == ' ' || line[len] == '\t'));
+}
+#endif /* !YP */
+
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..4d29692
--- /dev/null
+++ b/usr.bin/nfsstat/nfsstat.1
@@ -0,0 +1,91 @@
+.\" 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: nfsstat.1,v 1.6 1997/02/22 19:56:24 peter Exp $
+.\"
+.Dd June 6, 1993
+.Dt NFSSTAT 1
+.Os BSD 4.4
+.Sh NAME
+.Nm nfsstat
+.Nd display
+.Tn NFS
+statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Op Fl w Ar wait
+.Sh DESCRIPTION
+The
+.Nm
+command 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 sysctl 3 ,
+.Xr iostat 8 ,
+.Xr pstat 8 ,
+.Xr vmstat 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/usr.bin/nfsstat/nfsstat.c b/usr.bin/nfsstat/nfsstat.c
new file mode 100644
index 0000000..a53991a
--- /dev/null
+++ b/usr.bin/nfsstat/nfsstat.c
@@ -0,0 +1,405 @@
+/*
+ * 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.8 1997/02/22 19:56:25 peter Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/time.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:")) != -1)
+ 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_VFS;
+ 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..fad8f6c
--- /dev/null
+++ b/usr.bin/nice/nice.1
@@ -0,0 +1,118 @@
+.\" 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
+.\" $Id$
+.\"
+.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 not given
+.Nm
+assumed the value 10.
+The priority is a value in the range -20 to 20. The default priority
+is 0, priority 20 is the lowest possible.
+.Nm Nice
+will execute
+.Ar command
+at priority
+.Ar number
+relative to the priority
+of
+.Nm nice .
+Higher priorities than the
+current process priority can only 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 EXAMPLES
+.Pp
+$ nice -5 date
+.Pp
+Execute command
+.Sq date
+at priority 5 assuming the priority of the
+shell is 0.
+.Pp
+# nice -16 nice --35 date
+.Pp
+Execute command
+.Sq date
+at priority -19 assuming the priority of the
+shell is 0 and you are the super-user.
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr getpriority 2 ,
+.Xr setpriority 2 ,
+.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..300dc87
--- /dev/null
+++ b/usr.bin/nice/nice.c
@@ -0,0 +1,95 @@
+/*
+ * 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 (argc < 2)
+ usage();
+
+ 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, "usage: nice [-number] command [arguments]\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..0ebeb98
--- /dev/null
+++ b/usr.bin/nm/nm.1
@@ -0,0 +1,126 @@
+.\" 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
+.\" $Id$
+.\"
+.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 agnoprtuwW
+.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 t
+Output in tabular format more suitable for other programs digestive system.
+.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.
+.It Fl W
+Include an extra column in the output with a ``W'' indicating weak symbols.
+.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 I
+indirect reference symbol
+.It Li T
+text segment symbol
+.It Li U
+undefined
+.It Li W
+warn if next symbol is referenced
+.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 a.out 5 ,
+.Xr ar 5 ,
+.Xr stab 5
+.Sh HISTORY
+An
+.Nm nm
+command appeared in
+.At v1 .
diff --git a/usr.bin/nm/nm.1aout b/usr.bin/nm/nm.1aout
new file mode 100644
index 0000000..0ebeb98
--- /dev/null
+++ b/usr.bin/nm/nm.1aout
@@ -0,0 +1,126 @@
+.\" 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
+.\" $Id$
+.\"
+.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 agnoprtuwW
+.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 t
+Output in tabular format more suitable for other programs digestive system.
+.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.
+.It Fl W
+Include an extra column in the output with a ``W'' indicating weak symbols.
+.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 I
+indirect reference symbol
+.It Li T
+text segment symbol
+.It Li U
+undefined
+.It Li W
+warn if next symbol is referenced
+.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 a.out 5 ,
+.Xr ar 5 ,
+.Xr stab 5
+.Sh HISTORY
+An
+.Nm nm
+command appeared in
+.At v1 .
diff --git a/usr.bin/nm/nm.c b/usr.bin/nm/nm.c
new file mode 100644
index 0000000..9ee4b6e
--- /dev/null
+++ b/usr.bin/nm/nm.c
@@ -0,0 +1,684 @@
+/*
+ * 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 print_weak_symbols;
+int fcount;
+
+int rev, table;
+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))
+#define SYMBOL_BIND(x) (((x) >> 4) & 0xf)
+
+void *emalloc();
+void usage __P(( void ));
+int process_file __P(( char * ));
+int show_archive __P(( char *, FILE * ));
+int show_objfile __P(( char *, FILE * ));
+void print_symbol __P(( char *, struct nlist * ));
+char typeletter __P((u_char));
+
+/*
+ * main()
+ * parse command line, execute process_file() for each file
+ * specified on the command line.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ int ch, errors;
+
+ while ((ch = getopt(argc, argv, "agnoprtuwW")) != -1) {
+ 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 't':
+ table = 1;
+ break;
+ case 'u':
+ print_only_undefined_symbols = 1;
+ break;
+ case 'w':
+ ignore_bad_archive_entries = 0;
+ break;
+ case 'W':
+ print_weak_symbols = 1;
+ 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.
+ */
+int
+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 && !table)
+ (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
+ */
+int
+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 && !table)
+ {
+ 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 && !table)
+ (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.
+ */
+int
+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
+ */
+void
+print_symbol(objname, sym)
+ char *objname;
+ register struct nlist *sym;
+{
+ char *typestring();
+
+ if (table) {
+ printf("%s|", objname);
+ if (SYMBOL_TYPE(sym->n_type) != N_UNDF)
+ (void)printf("%08lx", sym->n_value);
+ (void)printf("|");
+ if (IS_DEBUGGER_SYMBOL(sym->n_type))
+ (void)printf("-|%02x %04x %5s|", sym->n_other,
+ sym->n_desc&0xffff, typestring(sym->n_type));
+ else {
+ putchar(typeletter(sym->n_type));
+ if (print_weak_symbols && SYMBOL_BIND(sym->n_other)== 2)
+ putchar('W');
+ putchar('|');
+ }
+
+ /* print the symbol's name */
+ (void)printf("%s\n",sym->n_un.n_name);
+ return;
+ }
+ 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 {
+ putchar(' ');
+ putchar(typeletter(sym->n_type));
+ if (print_weak_symbols) {
+ if (SYMBOL_BIND(sym->n_other) == 2)
+ putchar('W');
+ else
+ putchar(' ');
+ }
+ putchar(' ');
+ }
+
+ /* 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:
+ /* This one is overloaded. EXT = Filename, INT = warn refs */
+ return(IS_EXTERNAL(type) ? 'F' : 'w');
+ case N_INDR:
+ return(IS_EXTERNAL(type) ? 'I' : 'i');
+ case N_TEXT:
+ return(IS_EXTERNAL(type) ? 'T' : 't');
+ case N_UNDF:
+ return(IS_EXTERNAL(type) ? 'U' : 'u');
+ }
+ return('?');
+}
+
+int
+fname(a0, b0)
+ void *a0, *b0;
+{
+ struct nlist *a = a0, *b = b0;
+
+ return(strcmp(a->n_un.n_name, b->n_un.n_name));
+}
+
+int
+rname(a0, b0)
+ void *a0, *b0;
+{
+ struct nlist *a = a0, *b = b0;
+
+ return(strcmp(b->n_un.n_name, a->n_un.n_name));
+}
+
+int
+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);
+}
+
+void
+usage(void)
+{
+ (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/opieinfo/Makefile b/usr.bin/opieinfo/Makefile
new file mode 100644
index 0000000..1e3f977
--- /dev/null
+++ b/usr.bin/opieinfo/Makefile
@@ -0,0 +1,16 @@
+# $Id$
+#
+OPIE_DIST?= ${.CURDIR}/../../contrib/opie
+
+PROG= opieinfo
+SRCS= opieinfo.c
+MAN1= opieinfo.1
+
+CFLAGS+=-I${OPIE_DIST}
+
+DPADD= ${LIBOPIE} ${LIBMD}
+LDADD= -lopie -lmd
+
+.PATH: ${OPIE_DIST}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/opiekey/Makefile b/usr.bin/opiekey/Makefile
new file mode 100644
index 0000000..de04e8d
--- /dev/null
+++ b/usr.bin/opiekey/Makefile
@@ -0,0 +1,16 @@
+# $Id$
+#
+OPIE_DIST?= ${.CURDIR}/../../contrib/opie
+
+PROG= opiekey
+SRCS= opiekey.c
+MAN1= opiekey.1
+
+CFLAGS+=-I${OPIE_DIST}
+
+DPADD= ${LIBOPIE} ${LIBMD}
+LDADD= -lopie -lmd
+
+.PATH: ${OPIE_DIST}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/opiepasswd/Makefile b/usr.bin/opiepasswd/Makefile
new file mode 100644
index 0000000..bb52c82
--- /dev/null
+++ b/usr.bin/opiepasswd/Makefile
@@ -0,0 +1,16 @@
+# $Id$
+#
+OPIE_DIST?= ${.CURDIR}/../../contrib/opie
+
+PROG= opiepasswd
+SRCS= opiepasswd.c
+MAN1= opiepasswd.1
+
+CFLAGS+=-I${OPIE_DIST}
+
+DPADD= ${LIBOPIE} ${LIBMD}
+LDADD= -lopie -lmd
+
+.PATH: ${OPIE_DIST}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/pagesize/Makefile b/usr.bin/pagesize/Makefile
new file mode 100644
index 0000000..c66eae7
--- /dev/null
+++ b/usr.bin/pagesize/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.2 (Berkeley) 4/3/94
+
+MAN1= pagesize.1
+
+beforeinstall:
+ ${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..500f4e8
--- /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 3 .
+This program is useful in constructing portable
+shell scripts.
+.Sh SEE ALSO
+.Xr getpagesize 3
+.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..4c7aeea
--- /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
+# $Id$
+#
+
+PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH
+
+exec sysctl -n hw.pagesize
diff --git a/usr.bin/passwd/Makefile b/usr.bin/passwd/Makefile
new file mode 100644
index 0000000..49c303c
--- /dev/null
+++ b/usr.bin/passwd/Makefile
@@ -0,0 +1,79 @@
+# From: @(#)Makefile 8.3 (Berkeley) 4/2/94
+# $Id: Makefile,v 1.23 1997/02/22 19:56:34 peter Exp $
+
+PROG= passwd
+SRCS= local_passwd.c yppasswd_private_xdr.c yppasswd_comm.c yp_passwd.c \
+ passwd.c pw_copy.c pw_util.c pw_yp.c
+
+DPADD= ${LIBCRYPT} ${LIBRPCSVC} ${LIBUTIL}
+LDADD= -lcrypt -lrpcsvc -lutil
+.PATH: ${.CURDIR}/../../usr.bin/chpass ${.CURDIR}/../../usr.sbin/vipw \
+ ${.CURDIR}/../rlogin ${.CURDIR}/../../libexec/ypxfr \
+ ${.CURDIR}/../../usr.sbin/rpc.yppasswdd
+
+CFLAGS+= -DLOGIN_CAP -DCRYPT -DYP -I. -I${.CURDIR} \
+ -I${.CURDIR}/../../usr.sbin/vipw \
+ -I${.CURDIR}/../../usr.bin/chpass \
+ -I${.CURDIR}/../../libexec/ypxfr \
+ -I${.CURDIR}/../../usr.sbin/rpc.yppasswdd \
+ -Dyp_error=warnx -DLOGGING
+
+SRCS+= ypxfr_misc.c yp_clnt.c yppasswd_clnt.c
+
+CLEANFILES= yp.h yp_clnt.c yppasswd.h yppasswd_clnt.c \
+ yppasswd_private.h yppasswd_private_xdr.c
+
+RPCGEN= rpcgen -C
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/yp.x
+RPCSRC_PW= ${DESTDIR}/usr/include/rpcsvc/yppasswd.x
+RPCSRC_PRIV= ${.CURDIR}/../../usr.sbin/rpc.yppasswdd/yppasswd_private.x
+
+yp.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+yp_clnt.c: ${RPCSRC} yp.h
+ ${RPCGEN} -l -o ${.TARGET} ${RPCSRC}
+
+yppasswd.h: ${RPCSRC_PW}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC_PW}
+
+yppasswd_clnt.c: ${RPCSRC_PW} yppasswd.h
+ ${RPCGEN} -l -o ${.TARGET} ${RPCSRC_PW}
+
+yppasswd_private.h: ${RPCSRC_PRIV}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC_PRIV}
+
+yppasswd_private_xdr.c: ${RPCSRC_PRIV} yppasswd_private.h
+ ${RPCGEN} -c -o ${.TARGET} ${RPCSRC_PRIV}
+
+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} ${LIBUTIL}
+LDADD= -lkadm -lkrb -ldes -lcrypt -lrpcsvc -lcom_err -lutil
+DISTRIBUTION= krb
+.endif
+
+beforeinstall:
+.for i in passwd yppasswd
+ [ ! -e ${DESTDIR}${BINDIR}/$i ] || \
+ chflags noschg ${DESTDIR}${BINDIR}/$i
+.endfor
+
+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..5e8a4bc
--- /dev/null
+++ b/usr.bin/passwd/local_passwd.c
@@ -0,0 +1,225 @@
+/*-
+ * 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.
+ *
+ * $Id: local_passwd.c,v 1.17 1997/05/10 19:02:38 davidn Exp $
+ */
+
+#ifndef lint
+static const 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
+
+#ifdef LOGGING
+#include <syslog.h>
+#endif
+
+#ifdef LOGIN_CAP
+#ifdef AUTH_NONE /* multiple defs :-( */
+#undef AUTH_NONE
+#endif
+#include <login_cap.h>
+#endif
+
+#include "extern.h"
+
+static uid_t uid;
+int randinit;
+
+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, min_length = 6;
+ char *p, *t;
+#ifdef LOGIN_CAP
+ login_cap_t * lc;
+#endif
+ char buf[_PASSWORD_LEN+1], salt[10];
+ 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);
+ }
+
+#ifdef LOGIN_CAP
+ /*
+ * Determine minimum password length and next password change date.
+ * Note that even for NIS passwords, login_cap is still used.
+ */
+ if ((lc = login_getpwclass(pw)) != NULL) {
+ time_t period;
+
+ /* minpasswordlen capablity */
+ min_length = (int)login_getcapnum(lc, "minpasswordlen",
+ min_length, min_length);
+ /* passwordperiod capability */
+ period = login_getcaptime(lc, "passwordperiod", 0, 0);
+ if (period > (time_t)0) {
+ pw->pw_change = time(NULL) + period;
+ }
+ login_close(lc);
+ }
+#endif
+
+ 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) < min_length && (uid != 0 || ++tries < 2)) {
+ (void)printf("Please enter a password at least %d characters in length.\n", min_length);
+ 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 */
+ if (!randinit) {
+ randinit = 1;
+ srandomdev();
+ }
+#ifdef NEWSALT
+ salt[0] = _PASSWORD_EFMT1;
+ to64(&salt[1], (long)(29 * 25), 4);
+ to64(&salt[5], random(), 4);
+ salt[9] = '\0';
+#else
+ /* Make a good size salt for algoritms that can use it. */
+ gettimeofday(&tv,0);
+ if (strncmp(pw->pw_passwd, "$1$", 3)) {
+ /* DES Salt */
+ to64(&salt[0], random(), 3);
+ to64(&salt[3], tv.tv_usec, 3);
+ to64(&salt[6], tv.tv_sec, 2);
+ salt[8] = '\0';
+ }
+ else {
+ /* MD5 Salt */
+ strncpy(&salt[0], "$1$", 3);
+ to64(&salt[3], random(), 3);
+ to64(&salt[6], tv.tv_usec, 3);
+ 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 by
+ * default. If the user has a valid login class (or the default
+ * fallback exists), then the next password change date is set
+ * by getnewpasswd() according to the "passwordperiod" capability
+ * if one has been specified.
+ */
+ pw->pw_change = 0;
+ pw->pw_passwd = getnewpasswd(pw, 0);
+ pw_copy(pfd, tfd, pw);
+
+ if (!pw_mkdb(uname))
+ pw_error((char *)NULL, 0, 1);
+#ifdef LOGGING
+ syslog(LOG_DEBUG, "user %s changed their local password\n", uname);
+#endif
+ return (0);
+}
diff --git a/usr.bin/passwd/passwd.1 b/usr.bin/passwd/passwd.1
new file mode 100644
index 0000000..a595bfa
--- /dev/null
+++ b/usr.bin/passwd/passwd.1
@@ -0,0 +1,210 @@
+.\" 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
+.Nm yppasswd
+.Op Fl l
+.Op Fl y
+.Op Fl d Ar domain
+.Op Fl s Ar host
+.Op Fl o
+.Sh DESCRIPTION
+.Nm Passwd
+changes the user's local, Kerberos, or NIS 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 (which
+may be overridden using the
+.Xr login.cap 5
+"minpasswordlen" setting for a user's login class) 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.
+.Pp
+.El
+When changing local or NIS password, the next password change date
+is set according to "passwordperiod" capability in the user's
+login class.
+.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.
+.Pp
+When changing an NIS password, unprivileged users are required to provide
+their old password for authentication (the
+.Xr rpc.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, with one important exception: the password authentication is
+bypassed for the super-user on the NIS master server. This means that
+the super-user on the NIS master server can make unrestricted changes to
+anyone's NIS password. The super-user on NIS client systems and NIS slave
+servers still needs to provide a password before the update will be processed.
+.Pp
+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.
+.It Fl d Ar domain
+Specify what domain to use when changing an NIS password. By default,
+.Nm passwd
+assumes that the system default domain should be used. This flag is
+primarily for use by the superuser on the NIS master server: a single
+NIS server can support multiple domains. It is also possible that the
+domainname on the NIS master may not be set (it is not necessary for
+an NIS server to also be a client) in which case the
+.Nm passwd
+command needs to be told what domain to operate on.
+.It Fl s Ar host
+Specify the name of an NIS server. This option, in conjunction
+with the
+.Fl d
+option, can be used to change an NIS password on a non-local NIS
+server. When a domain is specified with the
+.Fl d
+option and
+.Nm passwd
+is unable to determine the name of the NIS master server (possibly because
+the local domainname isn't set), the name of the NIS master is assumed to
+be ``localhost''. This can be overriden with the
+.Fl s
+flag. The specified hostname need not be the name of an NIS master: the
+name of the NIS master for a given map can be determined by querying any
+NIS server (master or slave) in a domain, so specifying the name of a
+slave server will work equally well.
+.Pp
+.It Fl o
+Do not automatically override the password authentication checks for the
+super-user on the NIS master server; assume 'old' mode instead. This
+flag is of limited practical use but is useful for testing.
+.El
+.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
+.It Pa /etc/login.conf
+Login class capabilities database
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr kerberos 1 ,
+.Xr kinit 1 ,
+.Xr login 1 ,
+.Xr login.conf 5 ,
+.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..46562c6
--- /dev/null
+++ b/usr.bin/passwd/passwd.c
@@ -0,0 +1,243 @@
+/*
+ * 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.
+ *
+ * $id$
+ *
+ */
+
+#ifndef lint
+static const 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 const char sccsid[] = "From: @(#)passwd.c 8.3 (Berkeley) 4/2/94";
+static const char rcsid[] =
+ "$Id: passwd.c,v 1.11 1997/02/22 19:56:35 peter Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef YP
+#include <pwd.h>
+#include <pw_yp.h>
+#include <rpcsvc/yp.h>
+int __use_yp = 0;
+int yp_errno = YP_TRUE;
+extern int yp_passwd __P(( char * ));
+#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 "d:h:lysfoi:r:u:"
+#else
+#define OPTIONS "d:h:lysfo"
+#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], "yppasswd")) __use_yp = 1;
+#endif
+
+ while ((ch = getopt(argc, argv, OPTIONS)) != -1) {
+ 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;
+ case 'd': /* Specify NIS domain. */
+#ifdef PARANOID
+ if (!getuid()) {
+#endif
+ yp_domain = optarg;
+ if (yp_server == NULL)
+ yp_server = "localhost";
+#ifdef PARANOID
+ } else {
+ warnx("Only the super-user may use the -d flag.");
+ }
+#endif
+ break;
+ case 'h': /* Specify NIS server. */
+#ifdef PARANOID
+ if (!getuid()) {
+#endif
+ yp_server = optarg;
+#ifdef PARANOID
+ } else {
+ warnx("Only the super-user may use the -h flag.");
+ }
+#endif
+ break;
+ case 'o':
+ force_old++;
+ 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, 0, 0);
+ 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 && (yp_in_pw_file || __use_yp))
+ 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] [-o] [-d domain [-h host]] [user]\n");
+#else
+ (void)fprintf(stderr, "usage: passwd [-l] [-y] [-o] [-d domain \
+[-h host]] [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..3eab43a
--- /dev/null
+++ b/usr.bin/passwd/yp_passwd.c
@@ -0,0 +1,188 @@
+/*
+ * 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>
+#include "yppasswd_comm.h"
+
+extern char *getnewpasswd __P(( struct passwd * , int ));
+
+int
+yp_passwd(char *user)
+{
+ struct timeval timeout;
+ struct yppasswd yppasswd;
+ struct master_yppasswd master_yppasswd;
+ struct passwd *pw;
+ CLIENT *clnt;
+ struct rpc_err err;
+ char *master;
+ int *status = NULL;
+ uid_t uid;
+
+ _use_yp = 1;
+
+ uid = getuid();
+
+ if ((master = get_yp_master(1)) == NULL) {
+ warnx("failed to find NIS master server");
+ return(1);
+ }
+
+ /*
+ * It is presumed that by the time we get here, use_yp()
+ * has been called and that we have verified that the user
+ * actually exists. This being the case, the yp_password
+ * stucture has already been filled in for us.
+ */
+
+ /* Use the correct password */
+ pw = (struct passwd *)&yp_password;
+
+ if (pw->pw_uid != uid && uid != 0) {
+ warnx("Only the super-user may change account information \
+for other users");
+ return(1);
+ }
+
+ pw->pw_change = 0;
+
+ /* Initialize password information */
+ if (suser_override) {
+ master_yppasswd.newpw.pw_passwd = strdup(pw->pw_passwd);
+ master_yppasswd.newpw.pw_name = strdup(pw->pw_name);
+ master_yppasswd.newpw.pw_uid = pw->pw_uid;
+ master_yppasswd.newpw.pw_gid = pw->pw_gid;
+ master_yppasswd.newpw.pw_expire = pw->pw_expire;
+ master_yppasswd.newpw.pw_change = pw->pw_change;
+ master_yppasswd.newpw.pw_fields = pw->pw_fields;
+ master_yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos);
+ master_yppasswd.newpw.pw_dir = strdup(pw->pw_dir);
+ master_yppasswd.newpw.pw_shell = strdup(pw->pw_shell);
+ master_yppasswd.newpw.pw_class = pw->pw_class != NULL ?
+ strdup(pw->pw_class) : "";
+ master_yppasswd.oldpass = "";
+ master_yppasswd.domain = yp_domain;
+ } else {
+ 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);
+ yppasswd.oldpass = "";
+ }
+
+ if (suser_override)
+ printf("Changing NIS password for %s on %s in domain %s.\n",
+ pw->pw_name, master, yp_domain);
+ else
+ printf("Changing NIS password for %s on %s.\n",
+ pw->pw_name, master);
+
+ /* Get old password */
+
+ if(pw->pw_passwd[0] && !suser_override) {
+ yppasswd.oldpass = strdup(getpass("Old Password: "));
+ if (strcmp(crypt(yppasswd.oldpass, pw->pw_passwd),
+ pw->pw_passwd)) {
+ errx(1, "Sorry.");
+ }
+
+ }
+
+ if (suser_override) {
+ if ((master_yppasswd.newpw.pw_passwd = getnewpasswd(pw, 1)) == NULL)
+ return(1);
+ } else {
+ if ((yppasswd.newpw.pw_passwd = getnewpasswd(pw, 1)) == NULL)
+ return(1);
+ }
+
+ if (suser_override) {
+ if (senddat(&master_yppasswd)) {
+ warnx("failed to send request to rpc.yppasswdd");
+ return(1);
+ }
+ status = getresp();
+ } else {
+ if ((clnt = clnt_create(master, YPPASSWDPROG,
+ YPPASSWDVERS, "udp")) == NULL) {
+ warnx("failed to contact rpc.yppasswdd on host %s: %s",
+ master, clnt_spcreateerror(""));
+ return(1);
+ }
+ /*
+ * 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->cl_auth = authunix_create_default();
+
+ status = yppasswdproc_update_1(&yppasswd, clnt);
+ clnt_geterr(clnt, &err);
+
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ }
+
+ if ((!suser_override && err.re_status != RPC_SUCCESS) ||
+ status == NULL || *status) {
+ errx(1, "Failed to change NIS password: %s",
+ (err.re_status != RPC_SUCCESS && !suser_override) ?
+ clnt_sperrno(err.re_status) :
+ "rpc.yppasswdd returned error status");
+ }
+
+ printf("\nNIS password has%s been changed on %s.\n",
+ ((err.re_status != RPC_SUCCESS && !suser_override)
+ || status == NULL || *status) ?
+ " not" : "", master);
+
+ return ((err.re_status || status == NULL || *status));
+}
+#endif /* YP */
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..4155855
--- /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")) != -1)
+ 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..88eeeed
--- /dev/null
+++ b/usr.bin/pr/pr.1
@@ -0,0 +1,353 @@
+.\" 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
+.\" $Id$
+.\"
+.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.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/usr.bin/pr/pr.c b/usr.bin/pr/pr.c
new file mode 100644
index 0000000..ed729d8
--- /dev/null
+++ b/usr.bin/pr/pr.c
@@ -0,0 +1,1810 @@
+/*-
+ * 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 &&
+ ((dspace && (putchar('\n') == EOF)) ||
+ (putchar('\n') == EOF))) {
+ pfail();
+ return(1);
+ }
+ /*
+ * but honor the formfeed request
+ */
+ if (formfeed) {
+ if (putchar('\f') == 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:")) != -1) {
+ 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;
+ }
+ }
+
+ 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..cf33837
--- /dev/null
+++ b/usr.bin/printenv/printenv.c
@@ -0,0 +1,100 @@
+/*
+ * 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.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.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, "")) != -1)
+ 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..bdf9fa2
--- /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 octal, unsigned decimal,
+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..3444c20
--- /dev/null
+++ b/usr.bin/printf/printf.c
@@ -0,0 +1,409 @@
+/*
+ * 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 const 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 const 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>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef SHELL
+#define main printfcmd
+#include "bltin/bltin.h"
+#endif
+
+#define PF(f, func) { \
+ char *b = NULL; \
+ if (fieldwidth) \
+ if (precision) \
+ (void)asprintf(&b, f, fieldwidth, precision, func); \
+ else \
+ (void)asprintf(&b, f, fieldwidth, func); \
+ else if (precision) \
+ (void)asprintf(&b, f, precision, func); \
+ else \
+ (void)asprintf(&b, f, func); \
+ if (b) { \
+ (void)fputs(b, stdout); \
+ free(b); \
+ } \
+}
+
+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, "")) != -1)
+ 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..de2b4a8
--- /dev/null
+++ b/usr.bin/quota/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= quota
+BINOWN= root
+BINMODE=4555
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/quota/quota.1 b/usr.bin/quota/quota.1
new file mode 100644
index 0000000..b82cced
--- /dev/null
+++ b/usr.bin/quota/quota.1
@@ -0,0 +1,143 @@
+.\" 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.
+.\"
+.\" from: @(#)quota.1 8.1 (Berkeley) 6/6/93
+.\" $Id$
+.\"
+.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
+.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
+tries to report the quotas of all mounted filesystems.
+If the filesystem is mounted via
+.Nm NFS ,
+it will attempt to contact the
+.Xr rpc.rquotad 8
+daemon on the
+.Nm NFS
+server.
+For
+.Nm UFS
+filesystems, quotas must be turned on 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 ,
+.Xr rpc.rquotad 8
diff --git a/usr.bin/quota/quota.c b/usr.bin/quota/quota.c
new file mode 100644
index 0000000..77a165c
--- /dev/null
+++ b/usr.bin/quota/quota.c
@@ -0,0 +1,734 @@
+/*
+ * 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[] = "from: @(#)quota.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Disk quota reporting program.
+ */
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <ufs/ufs/quota.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fstab.h>
+#include <ctype.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <netdb.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpcsvc/rquota.h>
+
+char *qfname = QUOTAFILENAME;
+char *qfextension[] = INITQFNAMES;
+
+struct quotause {
+ struct quotause *next;
+ long flags;
+ struct dqblk dqblk;
+ char fsname[MAXPATHLEN + 1];
+};
+#define FOUND 0x01
+
+static char *timeprt __P((time_t seconds));
+static struct quotause *getprivs __P((long id, int quotatype));
+static void usage ();
+static void showuid(u_long uid);
+static void showgid(u_long gid);
+static int alldigits(char *s);
+static void showusrname(char *name);
+static void showgrpname(char *name);
+static void showquotas(int type, u_long id, char *name);
+static void heading(int type, u_long id, char *name, char *tag);
+static char *timeprt(time_t seconds);
+static struct quotause *getprivs(long id, int quotatype);
+static int ufshasquota(struct fstab *fs, int type, char **qfnamep);
+static int getufsquota(struct statfs *fst, struct fstab *fs,
+ struct quotause *qup, long id, int quotatype);
+static int getnfsquota(struct statfs *fst, struct fstab *fs,
+ struct quotause *qup, long id, int quotatype);
+static int callaurpc(char *host, int prognum, int versnum, int procnum,
+ xdrproc_t inproc, char *in, xdrproc_t outproc, char *out);
+static int alldigits(char *s);
+
+int qflag;
+int vflag;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ngroups;
+ gid_t mygid, gidset[NGROUPS];
+ int i, gflag = 0, uflag = 0;
+ char ch;
+ extern char *optarg;
+ extern int optind, errno;
+
+ while ((ch = getopt(argc, argv, "ugvq")) != -1) {
+ 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) {
+ mygid = getgid();
+ ngroups = getgroups(NGROUPS, gidset);
+ if (ngroups < 0) {
+ perror("quota: getgroups");
+ return(1);
+ }
+ showgid(mygid);
+ for (i = 0; i < ngroups; i++)
+ if (gidset[i] != mygid)
+ showgid(gidset[i]);
+ }
+ return(0);
+ }
+ if (uflag && gflag)
+ usage();
+ if (uflag) {
+ for (; argc > 0; argc--, argv++) {
+ if (alldigits(*argv))
+ showuid(atoi(*argv));
+ else
+ showusrname(*argv);
+ }
+ return(0);
+ }
+ if (gflag) {
+ for (; argc > 0; argc--, argv++) {
+ if (alldigits(*argv))
+ showgid(atoi(*argv));
+ else
+ showgrpname(*argv);
+ }
+ }
+ return(0);
+}
+
+static void
+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.
+ */
+static void
+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.
+ */
+static void
+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 %u): permission denied\n",
+ name, pwd->pw_uid);
+ return;
+ }
+ showquotas(USRQUOTA, pwd->pw_uid, name);
+}
+
+/*
+ * Print out quotas for a specified group identifier.
+ */
+static void
+showgid(gid)
+ u_long gid;
+{
+ struct group *grp = getgrgid(gid);
+ int ngroups;
+ gid_t mygid, gidset[NGROUPS];
+ register int i;
+ char *name;
+
+ if (grp == NULL)
+ name = "(no entry)";
+ else
+ name = grp->gr_name;
+ mygid = getgid();
+ ngroups = getgroups(NGROUPS, gidset);
+ if (ngroups < 0) {
+ perror("quota: getgroups");
+ return;
+ }
+ if (gid != mygid) {
+ for (i = 0; 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.
+ */
+static void
+showgrpname(name)
+ char *name;
+{
+ struct group *grp = getgrnam(name);
+ int ngroups;
+ gid_t mygid, gidset[NGROUPS];
+ register int i;
+
+ if (grp == NULL) {
+ fprintf(stderr, "quota: %s: unknown group\n", name);
+ return;
+ }
+ mygid = getgid();
+ ngroups = getgroups(NGROUPS, gidset);
+ if (ngroups < 0) {
+ perror("quota: getgroups");
+ return;
+ }
+ if (grp->gr_gid != mygid) {
+ for (i = 0; i < ngroups; i++)
+ if (grp->gr_gid == gidset[i])
+ break;
+ if (i >= ngroups && getuid() != 0) {
+ fprintf(stderr,
+ "quota: %s (gid %u): permission denied\n",
+ name, grp->gr_gid);
+ return;
+ }
+ }
+ showquotas(GRPQUOTA, grp->gr_gid, name);
+}
+
+static void
+showquotas(type, id, name)
+ int type;
+ u_long id;
+ char *name;
+{
+ register struct quotause *qup;
+ struct quotause *quplist;
+ char *msgi, *msgb, *nam;
+ int lines = 0;
+ 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, "");
+ nam = qup->fsname;
+ if (strlen(qup->fsname) > 15) {
+ printf("%s\n", qup->fsname);
+ nam = "";
+ }
+ printf("%15s%8lu%c%7lu%8lu%8s"
+ , nam
+ , (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");
+}
+
+static void
+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.
+ */
+static 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.
+ */
+static struct quotause *
+getprivs(id, quotatype)
+ register long id;
+ int quotatype;
+{
+ register struct quotause *qup, *quptail;
+ register struct fstab *fs;
+ struct quotause *quphead;
+ struct statfs *fst;
+ int nfst, i;
+
+ qup = quphead = (struct quotause *)0;
+
+ nfst = getmntinfo(&fst, MNT_WAIT);
+ if (nfst == 0) {
+ fprintf(stderr, "quota: no filesystems mounted!\n");
+ exit(2);
+ }
+ setfsent();
+ for (i=0; i<nfst; i++) {
+ if (qup == NULL) {
+ if ((qup = (struct quotause *)malloc(sizeof *qup))
+ == NULL) {
+ fprintf(stderr, "quota: out of memory\n");
+ exit(2);
+ }
+ }
+ if (fst[i].f_type == MOUNT_NFS) {
+ if (getnfsquota(&fst[i], NULL, qup, id, quotatype)
+ == 0)
+ continue;
+ } else if (fst[i].f_type == MOUNT_UFS) {
+ /*
+ * XXX
+ * UFS filesystems must be in /etc/fstab, and must
+ * indicate that they have quotas on (?!) This is quite
+ * unlike SunOS where quotas can be enabled/disabled
+ * on a filesystem independent of /etc/fstab, and it
+ * will still print quotas for them.
+ */
+ if ((fs = getfsspec(fst[i].f_mntfromname)) == NULL)
+ continue;
+ if (getufsquota(&fst[i], fs, qup, id, quotatype) == 0)
+ continue;
+ } else
+ continue;
+ strcpy(qup->fsname, fst[i].f_mntonname);
+ if (quphead == NULL)
+ quphead = qup;
+ else
+ quptail->next = qup;
+ quptail = qup;
+ quptail->next = 0;
+ qup = NULL;
+ }
+ if (qup)
+ free(qup);
+ endfsent();
+ return (quphead);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+static int
+ufshasquota(fs, type, qfnamep)
+ register struct fstab *fs;
+ int type;
+ char **qfnamep;
+{
+ static char initname, usrname[100], grpname[100];
+ static char buf[BUFSIZ];
+ char *opt, *cp;
+
+ 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);
+}
+
+static int
+getufsquota(fst, fs, qup, id, quotatype)
+ struct statfs *fst;
+ struct fstab *fs;
+ struct quotause *qup;
+ long id;
+ int quotatype;
+{
+ char *qfpathname;
+ int fd, qcmd;
+
+ qcmd = QCMD(Q_GETQUOTA, quotatype);
+ if (!ufshasquota(fs, quotatype, &qfpathname))
+ return (0);
+
+ if (quotactl(fs->fs_file, qcmd, id, (char *)&qup->dqblk) != 0) {
+ if ((fd = open(qfpathname, O_RDONLY)) < 0) {
+ perror(qfpathname);
+ return (0);
+ }
+ (void) lseek(fd, (off_t)(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);
+ return (0);
+ }
+ close(fd);
+ }
+ return (1);
+}
+
+static int
+getnfsquota(fst, fs, qup, id, quotatype)
+ struct statfs *fst;
+ struct fstab *fs;
+ struct quotause *qup;
+ long id;
+ int quotatype;
+{
+ struct getquota_args gq_args;
+ struct getquota_rslt gq_rslt;
+ struct dqblk *dqp = &qup->dqblk;
+ struct timeval tv;
+ char *cp;
+
+ if (fst->f_flags & MNT_LOCAL)
+ return (0);
+
+ /*
+ * rpc.rquotad does not support group quotas
+ */
+ if (quotatype != USRQUOTA)
+ return (0);
+
+ /*
+ * must be some form of "hostname:/path"
+ */
+ cp = strchr(fst->f_mntfromname, ':');
+ if (cp == NULL) {
+ fprintf(stderr, "cannot find hostname for %s\n",
+ fst->f_mntfromname);
+ return (0);
+ }
+
+ *cp = '\0';
+ if (*(cp+1) != '/') {
+ *cp = ':';
+ return (0);
+ }
+
+ gq_args.gqa_pathp = cp + 1;
+ gq_args.gqa_uid = id;
+ if (callaurpc(fst->f_mntfromname, RQUOTAPROG, RQUOTAVERS,
+ RQUOTAPROC_GETQUOTA, xdr_getquota_args, (char *)&gq_args,
+ xdr_getquota_rslt, (char *)&gq_rslt) != 0) {
+ *cp = ':';
+ return (0);
+ }
+
+ switch (gq_rslt.status) {
+ case Q_NOQUOTA:
+ break;
+ case Q_EPERM:
+ fprintf(stderr, "quota permission error, host: %s\n",
+ fst->f_mntfromname);
+ break;
+ case Q_OK:
+ gettimeofday(&tv, NULL);
+ /* blocks*/
+ dqp->dqb_bhardlimit =
+ gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit *
+ gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE;
+ dqp->dqb_bsoftlimit =
+ gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit *
+ gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE;
+ dqp->dqb_curblocks =
+ gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks *
+ gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE;
+ /* inodes */
+ dqp->dqb_ihardlimit =
+ gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit;
+ dqp->dqb_isoftlimit =
+ gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit;
+ dqp->dqb_curinodes =
+ gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles;
+ /* grace times */
+ dqp->dqb_btime =
+ tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft;
+ dqp->dqb_itime =
+ tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft;
+ *cp = ':';
+ return (1);
+ default:
+ fprintf(stderr, "bad rpc result, host: %s\n",
+ fst->f_mntfromname);
+ break;
+ }
+ *cp = ':';
+ return (0);
+}
+
+static int
+callaurpc(host, prognum, versnum, procnum, inproc, in, outproc, out)
+ char *host;
+ xdrproc_t inproc, outproc;
+ char *in, *out;
+ int prognum, versnum, procnum;
+{
+ struct sockaddr_in server_addr;
+ enum clnt_stat clnt_stat;
+ struct hostent *hp;
+ struct timeval timeout, tottimeout;
+
+ CLIENT *client = NULL;
+ int socket = RPC_ANYSOCK;
+
+ if ((hp = gethostbyname(host)) == NULL)
+ return ((int) RPC_UNKNOWNHOST);
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 6;
+ bcopy(hp->h_addr, &server_addr.sin_addr, hp->h_length);
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = 0;
+
+ if ((client = clntudp_create(&server_addr, prognum,
+ versnum, timeout, &socket)) == NULL)
+ return ((int) rpc_createerr.cf_stat);
+
+ client->cl_auth = authunix_create_default();
+ tottimeout.tv_sec = 25;
+ tottimeout.tv_usec = 0;
+ clnt_stat = clnt_call(client, procnum, inproc, in,
+ outproc, out, tottimeout);
+
+ return ((int) clnt_stat);
+}
+
+static int
+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..3b70630
--- /dev/null
+++ b/usr.bin/ranlib/build.c
@@ -0,0 +1,296 @@
+/*-
+ * 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive.h"
+
+extern int tmp __P(( void ));
+extern void error __P(( char * ));
+extern void badfmt __P(( void ));
+extern void settime __P(( int ));
+
+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));
+
+int
+build(void)
+{
+ 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, 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 = 0, *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..600e49e
--- /dev/null
+++ b/usr.bin/ranlib/misc.c
@@ -0,0 +1,110 @@
+/*-
+ * 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 <signal.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" */
+
+void error __P(( char * ));
+
+int
+tmp(void)
+{
+ 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);
+}
+
+void
+badfmt(void)
+{
+ errno = EFTYPE;
+ error(archive);
+}
+
+void
+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..bb419d6
--- /dev/null
+++ b/usr.bin/ranlib/ranlib.1
@@ -0,0 +1,89 @@
+.\" 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
+.\" $Id$
+.\"
+.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 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..bb419d6
--- /dev/null
+++ b/usr.bin/ranlib/ranlib.1aout
@@ -0,0 +1,89 @@
+.\" 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
+.\" $Id$
+.\"
+.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 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..d58a4cf
--- /dev/null
+++ b/usr.bin/ranlib/ranlib.c
@@ -0,0 +1,96 @@
+/*-
+ * 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>
+
+extern int build __P(( void ));
+extern int touch __P(( void ));
+void usage __P((void));
+
+CHDR chdr;
+u_int options; /* UNUSED -- keep open_archive happy */
+char *archive;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ int ch, eval, tflag;
+
+ tflag = 0;
+ while ((ch = getopt(argc, argv, "t")) != -1)
+ 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);
+}
+
+void
+usage(void)
+{
+ (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..68a86a4
--- /dev/null
+++ b/usr.bin/ranlib/touch.c
@@ -0,0 +1,91 @@
+/*-
+ * 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 */
+
+extern void error __P(( char * ));
+void settime __P(( int ));
+int touch __P(( void ));
+
+int
+touch(void)
+{
+ 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);
+}
+
+void
+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..637418b
--- /dev/null
+++ b/usr.bin/rdist/Makefile
@@ -0,0 +1,16 @@
+# @(#)Makefile 8.3 (Berkeley) 7/19/93
+
+PROG= rdist
+CFLAGS+=-I${.CURDIR}
+SRCS= docmd.c expand.c lookup.c main.c rshrcmd.c server.c
+SRCS+= gram.c
+CLEANFILES=y.tab.h gram.c
+
+# To use the old method, which requires setuid-root and all the baggage and
+# security holes that goes with it, uncomment:
+# CFLAGS+= -DDIRECT_RCMD
+# BINOWN= root
+# BINMODE=4555
+# INSTALLFLAGS=-fschg
+
+.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..cc484a2
--- /dev/null
+++ b/usr.bin/rdist/defs.h
@@ -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.
+ *
+ * @(#)defs.h 8.1 (Berkeley) 6/9/93
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+
+#include <dirent.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))
+
+/*
+ * RSH Time Out interval (in seconds).
+ * Should be long enough to allow rsh to even the slowest hosts.
+ */
+#define RTIMEOUT 180
+
+
+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[BUFSIZ]; /* general purpose buffer */
+extern char target[BUFSIZ]; /* target/source directory name */
+extern char *path_rsh; /* rsh command to use */
+
+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 *, int));
+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));
+int rshrcmd __P((char **, u_short, char *, char *, char *, int *));
diff --git a/usr.bin/rdist/docmd.c b/usr.bin/rdist/docmd.c
new file mode 100644
index 0000000..a5dc82c
--- /dev/null
+++ b/usr.bin/rdist/docmd.c
@@ -0,0 +1,694 @@
+/*
+ * 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$";
+#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 *));
+static int remotecmd __P((char *, char *, char *, char *));
+
+/*
+ * 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(%p, %s, %p)\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);
+ }
+ }
+}
+
+static int remotecmd(rhost, luser, ruser, cmd)
+ char *rhost;
+ char *luser, *ruser;
+ char *cmd;
+{
+ int desc;
+ static int port = -1;
+
+ if (debug) {
+ printf("local user = %s remote user = %s\n", luser, ruser);
+ printf("Remote command = '%s'\n", cmd);
+ }
+
+ (void) fflush(stdout);
+ (void) fflush(stderr);
+ (void) signal(SIGALRM, cleanup);
+ (void) alarm(RTIMEOUT);
+
+ if (geteuid() == 0 && strcmp(path_rsh, _PATH_RSH) == 0) {
+ if (debug)
+ printf("I am root, therefore direct rcmd\n");
+
+ (void) signal(SIGPIPE, cleanup);
+
+ if (port < 0) {
+ struct servent *sp;
+
+ if ((sp = getservbyname("shell", "tcp")) == NULL)
+ fatal("shell/tcp: unknown service");
+ port = sp->s_port;
+ }
+
+ desc = rcmd(&rhost, port, luser, ruser, cmd, 0);
+ } else {
+ if (debug)
+ printf("Remote shell command = '%s'\n", path_rsh);
+ (void) signal(SIGPIPE, SIG_IGN);
+ desc = rshrcmd(&rhost, -1, luser, ruser, cmd, 0);
+ if (desc > 0)
+ (void) signal(SIGPIPE, cleanup);
+ }
+
+ (void) alarm(0);
+
+ return(desc);
+}
+
+
+/*
+ * 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[];
+#if defined(DIRECT_RCMD)
+ extern int userid;
+#endif
+
+ 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) snprintf(buf, sizeof(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);
+#if defined(DIRECT_RCMD)
+ seteuid(0);
+ rem = rcmd(&rhost, port, user, ruser, buf, 0);
+ seteuid(userid);
+#else
+ rem = remotecmd(rhost, user, ruser, buf);
+#endif
+ 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, sizeof(target)) == 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 dirent *dp;
+ register char *cp;
+ char *otp;
+ int len;
+
+ if (debug)
+ printf("rcmptime(%p)\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) snprintf(buf, sizeof(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..497a551
--- /dev/null
+++ b/usr.bin/rdist/expand.c
@@ -0,0 +1,672 @@
+/*
+ * 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(%p, %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) {
+ snprintf(buf, sizeof(buf),
+ "%s%s%s", s, tp->n_name, tail);
+ expstr(buf);
+ }
+ return;
+ }
+ snprintf(buf, sizeof(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 dirent *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, maxlen)
+ char buf[];
+ register char *file;
+ int maxlen;
+{
+ register char *s1, *s2, *s3;
+ extern char homedir[];
+
+ if (strlen(file) >= maxlen)
+ return(NULL);
+ 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++) && s1 < buf+maxlen; )
+ ;
+ s2 = --s1;
+ if (s3 != NULL && s1 < buf+maxlen) {
+ s2++;
+ while ((*s1++ = *s3++) && s1 < buf+maxlen)
+ ;
+ }
+ if (s1 == buf+maxlen)
+ return(NULL);
+ return(s2);
+}
diff --git a/usr.bin/rdist/gram.y b/usr.bin/rdist/gram.y
new file mode 100644
index 0000000..09f6f68
--- /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, *next_h;
+
+ files = expand(files, E_VARS|E_SHELL);
+ hosts = expand(hosts, E_ALL);
+ for (h = hosts; h != NULL; next_h = h->n_next, free(h), h = next_h) {
+ /*
+ * 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..dc2f134
--- /dev/null
+++ b/usr.bin/rdist/lookup.c
@@ -0,0 +1,167 @@
+/*
+ * 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 = NULL;
+
+ 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, %p)\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)snprintf(buf, sizeof(buf),
+ "%s redefined", name);
+ yyerror(buf);
+ }
+ }
+ return(s->s_value);
+ }
+
+ if (action == LOOKUP) {
+ (void)snprintf(buf, sizeof(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..3badde7
--- /dev/null
+++ b/usr.bin/rdist/main.c
@@ -0,0 +1,335 @@
+/*
+ * 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 */
+char *path_rsh = _PATH_RSH; /* rsh (or equiv command) path */
+
+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 'P':
+ if (--argc <= 0)
+ usage();
+ path_rsh = *++argv;
+ break;
+
+ 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] [-P /path/to/rsh ] [-f distfile] [-d var=value]\n");
+ printf(" [-m host] [file ...]\n");
+ printf("or: rdist [-nqbhirvwyD] [-P /path/to/rsh ] -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 = NULL, *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..c772539
--- /dev/null
+++ b/usr.bin/rdist/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/9/93
+ */
+
+#include <paths.h>
+
+#define _PATH_RDIST "rdist"
+#define _PATH_RSH "/usr/bin/rsh"
diff --git a/usr.bin/rdist/rdist.1 b/usr.bin/rdist/rdist.1
new file mode 100644
index 0000000..087690a
--- /dev/null
+++ b/usr.bin/rdist/rdist.1
@@ -0,0 +1,419 @@
+.\" 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 P Ar rshcmd
+.Op Fl f Ar distfile
+.Op Fl d Ar var=value
+.Op Fl m Ar host
+.Op Ar name ...
+.Nm rdist
+.Op Fl nqbRhivwy
+.Op Fl P Ar rshcmd
+.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 P Ar rshcmd
+Alternative program to provide
+.Xr rsh 1 -like
+transport to the remote server. It must provide a binary-transparent path
+to the remote server, and must have a command argument syntax that is
+compatable with
+.Xr rsh 1 .
+.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 csh 1 ,
+.Xr sh 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/rshrcmd.c b/usr.bin/rdist/rshrcmd.c
new file mode 100644
index 0000000..6d2986b
--- /dev/null
+++ b/usr.bin/rdist/rshrcmd.c
@@ -0,0 +1,124 @@
+
+/*
+ * This is an rcmd() replacement originally by
+ * Chris Siebenmann <cks@utcc.utoronto.ca>.
+ */
+
+#ifndef lint
+static char RCSid[] =
+"$Id$";
+#endif
+
+#include "defs.h"
+
+#if !defined(DIRECT_RCMD)
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+static char *
+xbasename(s)
+ char *s;
+{
+ char *ret;
+
+ ret = strrchr(s, '/');
+ if (ret && ret[1])
+ return (ret + 1);
+ return s;
+}
+
+
+/*
+ * This is a replacement rcmd() function that uses the rsh(1c)
+ * program in place of a direct rcmd() function call so as to
+ * avoid having to be root.
+ */
+int
+rshrcmd(ahost, port, luser, ruser, cmd, fd2p)
+ char **ahost;
+ u_short port;
+ char *luser, *ruser, *cmd;
+ int *fd2p;
+{
+ int cpid;
+ struct hostent *hp;
+ int sp[2];
+
+ /* insure that we are indeed being used as we thought. */
+ if (fd2p != 0)
+ return -1;
+ /* validate remote hostname. */
+ hp = gethostbyname(*ahost);
+ if (hp == 0) {
+ error("%s: unknown host", *ahost);
+ return -1;
+ }
+ /* *ahost = hp->h_name; *//* This makes me nervous. */
+
+ /* get a socketpair we'll use for stdin and stdout. */
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) {
+ error("socketpair(AF_UNIX, SOCK_STREAM, 0) failed: %s.",
+ strerror(errno));
+ return -1;
+ }
+
+ cpid = fork();
+ if (cpid < 0) {
+ error("fork failed: %s.", strerror(errno));
+ return -1; /* error. */
+ }
+ if (cpid == 0) {
+ /* child. we use sp[1] to be stdin/stdout, and close
+ sp[0]. */
+ (void) close(sp[0]);
+ if (dup2(sp[1], 0) < 0 || dup2(0,1) < 0) {
+ error("dup2 failed: %s.", strerror(errno));
+ _exit(255);
+ }
+ /* fork again to lose parent. */
+ cpid = fork();
+ if (cpid < 0) {
+ error("fork to lose parent failed: %s.", strerror(errno));
+ _exit(255);
+ }
+ if (cpid > 0)
+ _exit(0);
+ /* in grandchild here. */
+
+ /*
+ * If we are rdist'ing to "localhost" as the same user
+ * as we are, then avoid running remote shell for efficiency.
+ */
+ if (strcmp(*ahost, "localhost") == 0 &&
+ strcmp(luser, ruser) == 0) {
+ execlp(_PATH_BSHELL, xbasename(_PATH_BSHELL), "-c",
+ cmd, (char *) NULL);
+ error("execlp %s failed: %s.", _PATH_BSHELL, strerror(errno));
+ } else {
+ execlp(path_rsh, xbasename(path_rsh),
+ *ahost, "-l", ruser, cmd, (char *) NULL);
+ error("execlp %s failed: %s.", path_rsh,
+ strerror(errno));
+ }
+ _exit(255);
+ }
+ if (cpid > 0) {
+ /* parent. close sp[1], return sp[0]. */
+ (void) close(sp[1]);
+ /* reap child. */
+ (void) wait(0);
+ return sp[0];
+ }
+ /*NOTREACHED*/
+ return (-1);
+}
+
+#endif /* !DIRECT_RCMD */
diff --git a/usr.bin/rdist/server.c b/usr.bin/rdist/server.c
new file mode 100644
index 0000000..37ae9c5
--- /dev/null
+++ b/usr.bin/rdist/server.c
@@ -0,0 +1,1594 @@
+/*
+ * 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) snprintf(buf, sizeof(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, sizeof(target)) == 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, sizeof(buf)) == 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, sizeof(target));
+ 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) snprintf(buf, sizeof(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 dirent *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)snprintf(user, sizeof(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)snprintf(group, sizeof(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) snprintf(buf, sizeof(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) snprintf(buf, sizeof(buf), "k%o %s %s\n",
+ opts, lp->pathname, rname);
+ else
+ (void) snprintf(buf, sizeof(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) snprintf(buf, sizeof(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) snprintf(buf, sizeof(buf), "k%o %s %s\n", opts,
+ lp->pathname, rname);
+ else
+ (void) snprintf(buf, sizeof(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) snprintf(buf, sizeof(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) snprintf(buf, sizeof(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, %p)\n", rname, opts, stp);
+
+ /*
+ * Check to see if the file exists on the remote machine.
+ */
+ (void) snprintf(buf, sizeof(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) snprintf(tp, sizeof(target) - (tp - target), "/%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) snprintf(buf, sizeof(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 = -1, 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) snprintf(buf + 1, sizeof(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) snprintf(tp, sizeof(target) - (tp - target), "/%s", cp);
+ cp = rindex(target, '/');
+ if (cp == NULL)
+ strcpy(new, tempname);
+ else if (cp == target)
+ (void) snprintf(new, sizeof(new), "/%s", tempname);
+ else {
+ *cp = '\0';
+ (void) snprintf(new, sizeof(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) snprintf(buf + 1, sizeof(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: if (f != -1) (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) snprintf(buf + 1, sizeof(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) snprintf(tp, sizeof(target) - (tp - target), "/%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) snprintf(buf, sizeof(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) snprintf(tp, sizeof(target) - (tp - target),
+ "/%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 dirent *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) snprintf(buf, sizeof(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) snprintf(cp, sizeof(buf) - 1,
+ "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 dirent *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) snprintf(cp, sizeof(buf) - 1, "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..2f8241f
--- /dev/null
+++ b/usr.bin/rev/rev.c
@@ -0,0 +1,110 @@
+/*-
+ * 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.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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, "")) != -1)
+ 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..9a2f5a3
--- /dev/null
+++ b/usr.bin/rlogin/kcmd.c
@@ -0,0 +1,314 @@
+/*
+ * 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 <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);
+ }
+
+ if (!(host_save = malloc(strlen(hp->h_name) + 1))) {
+ perror("malloc");
+ return -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, sizeof sin.sin_addr);
+#else
+ bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr, sizeof sin.sin_addr);
+#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,
+ sizeof sin.sin_addr);
+ 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..cb9c98f
--- /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 <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..eea0c59
--- /dev/null
+++ b/usr.bin/rlogin/rlogin.1
@@ -0,0 +1,205 @@
+.\" 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 login ,
+.Xr rsh 1 ,
+.Xr telnet 1 ,
+.Xr setsockopt 2 ,
+.Xr kerberos 3 ,
+.Xr krb_realmofhost 3 ,
+.Xr krb_sendauth 3 ,
+.Xr ruserok 3 ,
+.Xr tty 4 ,
+.Xr hosts 5 ,
+.Xr rlogind 8 ,
+.Xr rshd 8
+
+.Sh FILES
+.Bl -tag -width /etc/hosts -compact
+.It Pa /etc/hosts
+.It Pa /etc/hosts.equiv
+.It Pa $HOME/.rhosts
+.It Pa $HOME/.klogin
+.El
+
+.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..2ddc2b8
--- /dev/null
+++ b/usr.bin/rlogin/rlogin.c
@@ -0,0 +1,954 @@
+/*
+ * 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 <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"
+#define MAX_SPEED_LENGTH (sizeof("115200") - 1)
+};
+
+#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));
+void doit __P((long)) __dead2;
+void done __P((int)) __dead2;
+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));
+void usage __P((void)) __dead2;
+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)) != -1)
+ 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);
+ }
+
+#define MAX_TERM_LENGTH (sizeof(term) - 1 - MAX_SPEED_LENGTH - 1)
+
+ (void)strncpy(term, (p = getenv("TERM")) ? p : "network",
+ MAX_TERM_LENGTH);
+ term[MAX_TERM_LENGTH] = '\0';
+ 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_krb(&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);
+}
+
+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
+
+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..d0e4c14
--- /dev/null
+++ b/usr.bin/rpcgen/Makefile
@@ -0,0 +1,13 @@
+SRCS= rpc_main.c rpc_clntout.c rpc_cout.c rpc_hout.c rpc_parse.c \
+ rpc_sample.c rpc_scan.c rpc_svcout.c rpc_tblout.c rpc_util.c
+
+PROG= rpcgen
+MAN1= rpcgen.1
+
+#
+# This is a kludge to work around the fact that this program
+# uses 'inline' as a variable name.
+#
+CFLAGS+=-Dinline=rpcgen_inline
+
+.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..e4a9db7
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_clntout.c
@@ -0,0 +1,330 @@
+/*
+ * 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
+ */
+
+#ident "@(#)rpc_clntout.c 1.15 94/04/25 SMI"
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_clntout.c 1.11 89/02/22 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_clntout.c, Client-stub outputter for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsytsems, Inc.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <rpc/types.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+extern void pdeclaration __P(( char *, declaration *, int, char * ));
+void printarglist __P(( proc_list *, char *, char *, char *));
+static void write_program __P(( definition * ));
+static void printbody __P(( proc_list * ));
+
+static char RESULT[] = "clnt_res";
+
+
+#define DEFAULT_TIMEOUT 25 /* in seconds */
+
+
+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 void
+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");
+ if (mtflag == 0) {
+ ptype(proc->res_prefix, proc->res_type, 1);
+ f_print(fout, "*\n");
+ pvname(proc->proc_name, vp->vers_num);
+ printarglist(proc, RESULT, "clnt", "CLIENT *");
+ } else {
+ f_print(fout, "enum clnt_stat \n");
+ pvname(proc->proc_name, vp->vers_num);
+ printarglist(proc, RESULT, "clnt", "CLIENT *");
+
+ }
+ f_print(fout, "{\n");
+ printbody(proc);
+
+ f_print(fout, "}\n");
+ }
+ }
+}
+
+/*
+ * Writes out declarations of procedure's argument list.
+ * In either ANSI C style, in one of old rpcgen style (pass by reference),
+ * or new rpcgen style (multiple arguments, pass by value);
+ */
+
+/* sample addargname = "clnt"; sample addargtype = "CLIENT * " */
+
+void printarglist(proc, result, addargname, addargtype)
+ proc_list *proc;
+ char *result;
+ char* addargname, * addargtype;
+{
+
+ decl_list *l;
+
+ if (!newstyle) {
+ /* old style: always pass argument by reference */
+ if (Cflag) { /* C++ style heading */
+ f_print(fout, "(");
+ ptype(proc->args.decls->decl.prefix,
+ proc->args.decls->decl.type, 1);
+
+ if (mtflag) {/* Generate result field */
+ f_print(fout, "*argp, ");
+ ptype(proc->res_prefix, proc->res_type, 1);
+ f_print(fout, "*%s, %s%s)\n",
+ result, addargtype, addargname);
+ } else
+ f_print(fout, "*argp, %s%s)\n", addargtype, addargname);
+ } else {
+ if (!mtflag)
+ f_print(fout, "(argp, %s)\n", addargname);
+ else
+ f_print(fout, "(argp, %s, %s)\n",
+ result, addargname);
+ f_print(fout, "\t");
+ ptype(proc->args.decls->decl.prefix,
+ proc->args.decls->decl.type, 1);
+ f_print(fout, "*argp;\n");
+ if (mtflag) {
+ f_print(fout, "\t");
+ ptype(proc->res_prefix, proc->res_type, 1);
+ f_print(fout, "*%s;\n", result);
+ }
+ }
+ } else if (streq(proc->args.decls->decl.type, "void")) {
+ /* newstyle, 0 argument */
+ if (mtflag) {
+ f_print(fout, "(");
+
+
+ if (Cflag) {
+ ptype(proc->res_prefix, proc->res_type, 1);
+ f_print(fout, "*%s, %s%s)\n",
+ result, addargtype, addargname);
+ }
+ else
+ f_print(fout, "(%s)\n", addargname);
+
+ } else
+ if (Cflag)
+ f_print(fout, "(%s%s)\n", addargtype, addargname);
+ else
+ f_print(fout, "(%s)\n", addargname);
+ } else {
+ /* new style, 1 or multiple arguments */
+ if (!Cflag) {
+ f_print(fout, "(");
+ for (l = proc->args.decls; l != NULL; l = l->next)
+ f_print(fout, "%s, ", l->decl.name);
+ if (mtflag)
+ f_print(fout, "%s, ", result);
+
+ f_print(fout, "%s)\n", addargname);
+ for (l = proc->args.decls; l != NULL; l = l->next) {
+ pdeclaration(proc->args.argname,
+ &l->decl, 1, ";\n");
+ }
+ if (mtflag) {
+ f_print(fout, "\t");
+ ptype(proc->res_prefix, proc->res_type, 1);
+ f_print(fout, "*%s;\n", result);
+ }
+
+ } else { /* C++ style header */
+ f_print(fout, "(");
+ for (l = proc->args.decls; l != NULL; l = l->next) {
+ pdeclaration(proc->args.argname, &l->decl, 0,
+ ", ");
+ }
+ if (mtflag) {
+ ptype(proc->res_prefix, proc->res_type, 1);
+ f_print(fout, "*%s, ", result);
+
+ }
+ f_print(fout, "%s%s)\n", addargtype, addargname);
+ }
+ }
+
+ if (!Cflag)
+ f_print(fout, "\t%s%s;\n", addargtype, addargname);
+}
+
+
+
+static char *
+ampr(type)
+ char *type;
+{
+ if (isvectordef(type, REL_ALIAS)) {
+ return ("");
+ } else {
+ return ("&");
+ }
+}
+
+static void
+printbody(proc)
+ proc_list *proc;
+{
+ decl_list *l;
+ bool_t args2 = (proc->arg_num > 1);
+
+ /*
+ * For new style with multiple arguments, need a structure in which
+ * to stuff the arguments.
+ */
+
+
+ if (newstyle && args2) {
+ f_print(fout, "\t%s", proc->args.argname);
+ f_print(fout, " arg;\n");
+ }
+ if (!mtflag) {
+ 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, "%s;\n", RESULT);
+ f_print(fout, "\n");
+ f_print(fout, "\tmemset((char *)%s%s, 0, sizeof (%s));\n",
+ ampr(proc->res_type), RESULT, RESULT);
+
+ }
+ if (newstyle && !args2 &&
+ (streq(proc->args.decls->decl.type, "void"))) {
+ /* newstyle, 0 arguments */
+
+ if (mtflag)
+ f_print(fout, "\t return ");
+ else
+ f_print(fout, "\t if ");
+
+ f_print(fout,
+ "(clnt_call(clnt, %s,\n\t\t(xdrproc_t) xdr_void, ",
+ proc->proc_name);
+ f_print(fout,
+ "(caddr_t) NULL,\n\t\t(xdrproc_t) xdr_%s, (caddr_t) %s%s,",
+ stringfix(proc->res_type), (mtflag)?"":ampr(proc->res_type),
+ RESULT);
+
+ if (mtflag)
+ f_print(fout, "\n\t\tTIMEOUT));\n}\n");
+ else
+ f_print(fout, "\n\t\tTIMEOUT) != RPC_SUCCESS) {\n");
+
+ } else if (newstyle && args2) {
+ /*
+ * Newstyle, multiple arguments
+ * stuff arguments into structure
+ */
+ for (l = proc->args.decls; l != NULL; l = l->next) {
+ f_print(fout, "\targ.%s = %s;\n",
+ l->decl.name, l->decl.name);
+ }
+ if (mtflag)
+ f_print(fout, "\treturn ");
+ else
+ f_print(fout, "\tif ");
+ f_print(fout,
+ "(clnt_call(clnt, %s,\n\t\t(xdrproc_t) xdr_%s",
+ proc->proc_name,proc->args.argname);
+ f_print(fout,
+ ", (caddr_t) &arg,\n\t\t(xdrproc_t) xdr_%s, (caddr_t) %s%s,",
+ stringfix(proc->res_type), (mtflag)?"":ampr(proc->res_type),
+ RESULT);
+ if (mtflag)
+ f_print(fout, "\n\t\tTIMEOUT));\n");
+ else
+ f_print(fout, "\n\t\tTIMEOUT) != RPC_SUCCESS) {\n");
+ } else { /* single argument, new or old style */
+ if (!mtflag)
+ f_print(fout,
+ "\tif (clnt_call(clnt, %s,\n\t\t(xdrproc_t) xdr_%s, (caddr_t) %s%s,\n\t\t(xdrproc_t) xdr_%s, (caddr_t) %s%s,\n\t\tTIMEOUT) != RPC_SUCCESS) {\n",
+ proc->proc_name,
+ stringfix(proc->args.decls->decl.type),
+ (newstyle ? "&" : ""),
+ (newstyle ? proc->args.decls->decl.name : "argp"),
+ stringfix(proc->res_type), ampr(proc->res_type),
+ RESULT);
+ else
+
+ f_print(fout,
+ "\treturn (clnt_call(clnt, %s,\n\t\t(xdrproc_t) xdr_%s, (caddr_t) %s%s,\n\t\t(xdrproc_t) xdr_%s, (caddr_t) %s%s,\n\t\tTIMEOUT));\n",
+ proc->proc_name,
+ stringfix(proc->args.decls->decl.type),
+ (newstyle ? "&" : ""),
+ (newstyle ? proc->args.decls->decl.name : "argp"),
+ stringfix(proc->res_type), "",
+ RESULT);
+ }
+ if (!mtflag) {
+ f_print(fout, "\t\treturn (NULL);\n");
+ f_print(fout, "\t}\n");
+
+ if (streq(proc->res_type, "void")) {
+ f_print(fout, "\treturn ((void *)%s%s);\n",
+ ampr(proc->res_type), RESULT);
+ } else {
+ f_print(fout, "\treturn (%s%s);\n",
+ ampr(proc->res_type), RESULT);
+ }
+ }
+}
diff --git a/usr.bin/rpcgen/rpc_cout.c b/usr.bin/rpcgen/rpc_cout.c
new file mode 100644
index 0000000..7445fd7
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_cout.c
@@ -0,0 +1,758 @@
+/*
+ * 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
+ */
+
+#ident "@(#)rpc_cout.c 1.14 93/07/05 SMI"
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_cout.c 1.13 89/02/22 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_cout.c, XDR routine outputter for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+static void print_header __P(( definition * ));
+static void print_trailer __P(( void ));
+static void print_stat __P(( int , declaration * ));
+static void emit_enum __P(( definition * ));
+static void emit_program __P(( definition * ));
+static void emit_union __P(( definition * ));
+static void emit_struct __P(( definition * ));
+static void emit_typedef __P(( definition * ));
+static void emit_inline __P(( int, declaration *, int ));
+static void emit_single_in_line __P(( int, declaration *, int, relation ));
+
+/*
+ * Emit the C-routine for the given definition
+ */
+void
+emit(def)
+ definition *def;
+{
+ if (def->def_kind == DEF_CONST) {
+ return;
+ }
+ if (def->def_kind == DEF_PROGRAM) {
+ emit_program(def);
+ return;
+ }
+ if (def->def_kind == DEF_TYPEDEF) {
+ /*
+ * now we need to handle declarations like
+ * struct typedef foo foo;
+ * since we dont want this to be expanded into 2 calls to xdr_foo
+ */
+
+ if (strcmp(def->def.ty.old_type, def->def_name) == 0)
+ 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;
+ /* DEF_CONST and DEF_PROGRAM have already been handled */
+ default:
+ }
+ print_trailer();
+}
+
+static int
+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 int
+undefined(type)
+ char *type;
+{
+ definition *def;
+
+ def = (definition *) FINDVAL(defined, type, findtype);
+ return (def == NULL);
+}
+
+
+static void
+print_generic_header(procname, pointerp)
+ char* procname;
+ int pointerp;
+{
+ f_print(fout, "\n");
+ f_print(fout, "bool_t\n");
+ if (Cflag) {
+ f_print(fout, "xdr_%s(", procname);
+ f_print(fout, "register XDR *xdrs, ");
+ f_print(fout, "%s ", procname);
+ if (pointerp)
+ f_print(fout, "*");
+ f_print(fout, "objp)\n{\n\n");
+ } else {
+ f_print(fout, "xdr_%s(xdrs, objp)\n", procname);
+ f_print(fout, "\tregister XDR *xdrs;\n");
+ f_print(fout, "\t%s ", procname);
+ if (pointerp)
+ f_print(fout, "*");
+ f_print(fout, "objp;\n{\n\n");
+ }
+}
+
+static void
+print_header(def)
+ definition *def;
+{
+ print_generic_header(def->def_name,
+ def->def_kind != DEF_TYPEDEF ||
+ !isvectordef(def->def.ty.old_type,
+ def->def.ty.rel));
+ /* Now add Inline support */
+
+ if (inline == 0)
+ return;
+ /* May cause lint to complain. but ... */
+ f_print(fout, "\tregister long *buf;\n\n");
+}
+
+static void
+print_prog_header(plist)
+ proc_list *plist;
+{
+ print_generic_header(plist->args.argname, 1);
+}
+
+static void
+print_trailer()
+{
+ f_print(fout, "\treturn (TRUE);\n");
+ f_print(fout, "}\n");
+}
+
+
+static void
+print_ifopen(indent, name)
+ int indent;
+ char *name;
+{
+ tabify(fout, indent);
+ f_print(fout, "if (!xdr_%s(xdrs", name);
+}
+
+static void
+print_ifarg(arg)
+ char *arg;
+{
+ f_print(fout, ", %s", arg);
+}
+
+static void
+print_ifsizeof(indent, prefix, type)
+ int indent;
+ char *prefix;
+ char *type;
+{
+ if (indent) {
+ f_print(fout, ",\n");
+ tabify(fout, indent);
+ } else {
+ f_print(fout, ", ");
+ }
+ if (streq(type, "bool")) {
+ f_print(fout, "sizeof (bool_t), (xdrproc_t) xdr_bool");
+ } else {
+ f_print(fout, "sizeof (");
+ if (undefined(type) && prefix) {
+ f_print(fout, "%s ", prefix);
+ }
+ f_print(fout, "%s), (xdrproc_t) xdr_%s", type, type);
+ }
+}
+
+static void
+print_ifclose(indent)
+ int indent;
+{
+ f_print(fout, "))\n");
+ tabify(fout, indent);
+ f_print(fout, "\treturn (FALSE);\n");
+}
+
+static void
+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(0, 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(indent + 1, 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(indent + 1, prefix, type);
+ }
+ break;
+ case REL_ALIAS:
+ print_ifopen(indent, type);
+ print_ifarg(objname);
+ break;
+ }
+ print_ifclose(indent);
+}
+
+/* ARGSUSED */
+static void
+emit_enum(def)
+ definition *def;
+{
+ print_ifopen(1, "enum");
+ print_ifarg("(enum_t *)objp");
+ print_ifclose(1);
+}
+
+static void
+emit_program(def)
+ definition *def;
+{
+ decl_list *dl;
+ version_list *vlist;
+ proc_list *plist;
+
+ for (vlist = def->def.pr.versions; vlist != NULL; vlist = vlist->next)
+ for (plist = vlist->procs; plist != NULL; plist = plist->next) {
+ if (!newstyle || plist->arg_num < 2)
+ continue; /* old style, or single argument */
+ print_prog_header(plist);
+ for (dl = plist->args.decls; dl != NULL;
+ dl = dl->next)
+ print_stat(1, &dl->decl);
+ print_trailer();
+ }
+}
+
+
+static void
+emit_union(def)
+ definition *def;
+{
+ declaration *dflt;
+ case_list *cl;
+ declaration *cs;
+ char *object;
+ char *vecformat = "objp->%s_u.%s";
+ char *format = "&objp->%s_u.%s";
+
+ print_stat(1, &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) {
+
+ f_print(fout, "\tcase %s:\n", cl->case_name);
+ if (cl->contflag == 1) /* a continued case statement */
+ continue;
+ cs = &cl->case_decl;
+ if (!streq(cs->type, "void")) {
+ object = alloc(strlen(def->def_name) + strlen(format) +
+ strlen(cs->name) + 1);
+ if (isvectordef (cs->type, cs->rel)) {
+ s_print(object, vecformat, def->def_name,
+ cs->name);
+ } else {
+ 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);
+ if (isvectordef (dflt->type, dflt->rel)) {
+ s_print(object, vecformat, def->def_name,
+ dflt->name);
+ } else {
+ 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\tbreak;\n");
+ }
+ } else {
+ f_print(fout, "\tdefault:\n");
+ f_print(fout, "\t\treturn (FALSE);\n");
+ }
+
+ f_print(fout, "\t}\n");
+}
+
+static void
+inline_struct(def, flag)
+definition *def;
+int flag;
+{
+ decl_list *dl;
+ int i, size;
+ decl_list *cur, *psav;
+ bas_type *ptr;
+ char *sizestr, *plus;
+ char ptemp[256];
+ int indent = 1;
+
+ if (flag == PUT)
+ f_print(fout, "\n\tif (xdrs->x_op == XDR_ENCODE) {\n");
+ else
+ f_print(fout, "\t\treturn (TRUE);\n\t} else if (xdrs->x_op == XDR_DECODE) {\n");
+
+ i = 0;
+ size = 0;
+ sizestr = NULL;
+ for (dl = def->def.st.decls; dl != NULL; dl = dl->next) { /* xxx */
+ /* now walk down the list and check for basic types */
+ if ((dl->decl.prefix == NULL) &&
+ ((ptr = find_type(dl->decl.type)) != NULL) &&
+ ((dl->decl.rel == REL_ALIAS) ||
+ (dl->decl.rel == REL_VECTOR))){
+ if (i == 0)
+ cur = dl;
+ i++;
+
+ if (dl->decl.rel == REL_ALIAS)
+ size += ptr->length;
+ else {
+ /* this code is required to handle arrays */
+ if (sizestr == NULL)
+ plus = "";
+ else
+ plus = " + ";
+
+ if (ptr->length != 1)
+ s_print(ptemp, "%s%s * %d",
+ plus, dl->decl.array_max,
+ ptr->length);
+ else
+ s_print(ptemp, "%s%s", plus,
+ dl->decl.array_max);
+
+ /* now concatenate to sizestr !!!! */
+ if (sizestr == NULL)
+ sizestr = strdup(ptemp);
+ else{
+ sizestr = realloc(sizestr,
+ strlen(sizestr)
+ +strlen(ptemp)+1);
+ if (sizestr == NULL){
+ f_print(stderr,
+ "Fatal error : no memory\n");
+ crash();
+ };
+ sizestr = strcat(sizestr, ptemp);
+ /* build up length of array */
+ }
+ }
+ } else {
+ if (i > 0)
+ if (sizestr == NULL && size < inline){
+ /*
+ * don't expand into inline code
+ * if size < inline
+ */
+ while (cur != dl){
+ print_stat(indent + 1, &cur->decl);
+ cur = cur->next;
+ }
+ } else {
+ /* were already looking at a xdr_inlineable structure */
+ tabify(fout, indent + 1);
+ if (sizestr == NULL)
+ f_print(fout, "buf = XDR_INLINE(xdrs, %d * BYTES_PER_XDR_UNIT);",
+ size);
+ else
+ if (size == 0)
+ f_print(fout,
+ "buf = XDR_INLINE(xdrs, (%s) * BYTES_PER_XDR_UNIT);",
+ sizestr);
+ else
+ f_print(fout,
+ "buf = XDR_INLINE(xdrs, (%d + (%s)) * BYTES_PER_XDR_UNIT);",
+ size, sizestr);
+
+ f_print(fout, "\n");
+ tabify(fout, indent + 1);
+ f_print(fout,
+ "if (buf == NULL) {\n");
+
+ psav = cur;
+ while (cur != dl){
+ print_stat(indent + 2, &cur->decl);
+ cur = cur->next;
+ }
+
+ f_print(fout, "\n\t\t} else {\n");
+
+ cur = psav;
+ while (cur != dl){
+ emit_inline(indent + 2, &cur->decl, flag);
+ cur = cur->next;
+ }
+
+ tabify(fout, indent + 1);
+ f_print(fout, "}\n");
+ }
+ size = 0;
+ i = 0;
+ sizestr = NULL;
+ print_stat(indent + 1, &dl->decl);
+ }
+ }
+
+ if (i > 0)
+ if (sizestr == NULL && size < inline){
+ /* don't expand into inline code if size < inline */
+ while (cur != dl){
+ print_stat(indent + 1, &cur->decl);
+ cur = cur->next;
+ }
+ } else {
+ /* were already looking at a xdr_inlineable structure */
+ if (sizestr == NULL)
+ f_print(fout, "\t\tbuf = XDR_INLINE(xdrs, %d * BYTES_PER_XDR_UNIT);",
+ size);
+ else
+ if (size == 0)
+ f_print(fout,
+ "\t\tbuf = XDR_INLINE(xdrs, (%s) * BYTES_PER_XDR_UNIT);",
+ sizestr);
+ else
+ f_print(fout,
+ "\t\tbuf = XDR_INLINE(xdrs, (%d + (%s)) * BYTES_PER_XDR_UNIT);",
+ size, sizestr);
+
+ f_print(fout, "\n\t\tif (buf == NULL) {\n");
+ psav = cur;
+ while (cur != NULL){
+ print_stat(indent + 2, &cur->decl);
+ cur = cur->next;
+ }
+ f_print(fout, "\t\t} else {\n");
+
+ cur = psav;
+ while (cur != dl){
+ emit_inline(indent + 2, &cur->decl, flag);
+ cur = cur->next;
+ }
+ f_print(fout, "\t\t}\n");
+ }
+}
+
+static void
+emit_struct(def)
+ definition *def;
+{
+ decl_list *dl;
+ int j, size, flag;
+ bas_type *ptr;
+ int can_inline;
+
+ if (inline == 0) {
+ /* No xdr_inlining at all */
+ for (dl = def->def.st.decls; dl != NULL; dl = dl->next)
+ print_stat(1, &dl->decl);
+ return;
+ }
+
+ for (dl = def->def.st.decls; dl != NULL; dl = dl->next)
+ if (dl->decl.rel == REL_VECTOR){
+ f_print(fout, "\tint i;\n");
+ break;
+ }
+
+ size = 0;
+ can_inline = 0;
+ /*
+ * Make a first pass and see if inling is possible.
+ */
+ for (dl = def->def.st.decls; dl != NULL; dl = dl->next)
+ if ((dl->decl.prefix == NULL) &&
+ ((ptr = find_type(dl->decl.type)) != NULL) &&
+ ((dl->decl.rel == REL_ALIAS)||
+ (dl->decl.rel == REL_VECTOR))){
+ if (dl->decl.rel == REL_ALIAS)
+ size += ptr->length;
+ else {
+ can_inline = 1;
+ break; /* can be inlined */
+ }
+ } else {
+ if (size >= inline){
+ can_inline = 1;
+ break; /* can be inlined */
+ }
+ size = 0;
+ }
+ if (size >= inline)
+ can_inline = 1;
+
+ if (can_inline == 0){ /* can not inline, drop back to old mode */
+ for (dl = def->def.st.decls; dl != NULL; dl = dl->next)
+ print_stat(1, &dl->decl);
+ return;
+ }
+
+ flag = PUT;
+ for (j = 0; j < 2; j++){
+ inline_struct(def, flag);
+ if (flag == PUT)
+ flag = GET;
+ }
+
+ f_print(fout, "\t\treturn (TRUE);\n\t}\n\n");
+
+ /* now take care of XDR_FREE case */
+
+ for (dl = def->def.st.decls; dl != NULL; dl = dl->next)
+ print_stat(1, &dl->decl);
+
+}
+
+static void
+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 void
+print_stat(indent, dec)
+ int indent;
+ 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(indent, prefix, type, rel, amax, name, dec->name);
+}
+
+
+char *upcase ();
+
+static void
+emit_inline(indent, decl, flag)
+int indent;
+declaration *decl;
+int flag;
+{
+ switch (decl->rel) {
+ case REL_ALIAS :
+ emit_single_in_line(indent, decl, flag, REL_ALIAS);
+ break;
+ case REL_VECTOR :
+ tabify(fout, indent);
+ f_print(fout, "{\n");
+ tabify(fout, indent + 1);
+ f_print(fout, "register %s *genp;\n\n", decl->type);
+ tabify(fout, indent + 1);
+ f_print(fout,
+ "for (i = 0, genp = objp->%s;\n", decl->name);
+ tabify(fout, indent + 2);
+ f_print(fout, "i < %s; i++) {\n", decl->array_max);
+ emit_single_in_line(indent + 2, decl, flag, REL_VECTOR);
+ tabify(fout, indent + 1);
+ f_print(fout, "}\n");
+ tabify(fout, indent);
+ f_print(fout, "}\n");
+ default:
+ }
+}
+
+static void
+emit_single_in_line(indent, decl, flag, rel)
+int indent;
+declaration *decl;
+int flag;
+relation rel;
+{
+ char *upp_case;
+ int freed = 0;
+
+ tabify(fout, indent);
+ if (flag == PUT)
+ f_print(fout, "IXDR_PUT_");
+ else
+ if (rel == REL_ALIAS)
+ f_print(fout, "objp->%s = IXDR_GET_", decl->name);
+ else
+ f_print(fout, "*genp++ = IXDR_GET_");
+
+ upp_case = upcase(decl->type);
+
+ /* hack - XX */
+ if (strcmp(upp_case, "INT") == 0)
+ {
+ free(upp_case);
+ freed = 1;
+ upp_case = "LONG";
+ }
+
+ if (strcmp(upp_case, "U_INT") == 0)
+ {
+ free(upp_case);
+ freed = 1;
+ upp_case = "U_LONG";
+ }
+ if (flag == PUT)
+ if (rel == REL_ALIAS)
+ f_print(fout,
+ "%s(buf, objp->%s);\n", upp_case, decl->name);
+ else
+ f_print(fout, "%s(buf, *genp++);\n", upp_case);
+
+ else
+ f_print(fout, "%s(buf);\n", upp_case);
+ if (!freed)
+ free(upp_case);
+}
+
+char *upcase(str)
+char *str;
+{
+ char *ptr, *hptr;
+
+ ptr = (char *)malloc(strlen(str)+1);
+ if (ptr == (char *) NULL)
+ {
+ f_print(stderr, "malloc failed\n");
+ exit(1);
+ };
+
+ hptr = ptr;
+ while (*str != '\0')
+ *ptr++ = toupper(*str++);
+
+ *ptr = '\0';
+ return (hptr);
+}
diff --git a/usr.bin/rpcgen/rpc_hout.c b/usr.bin/rpcgen/rpc_hout.c
new file mode 100644
index 0000000..8dce77de
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_hout.c
@@ -0,0 +1,599 @@
+/*
+ * 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
+ */
+
+#ident "@(#)rpc_hout.c 1.16 94/04/25 SMI"
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_hout.c 1.12 89/02/22 (C) 1987 SMI";
+#endif
+
+/*
+ * 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_parse.h"
+#include "rpc_util.h"
+
+void storexdrfuncdecl __P(( char *, int ));
+static void pconstdef __P(( definition * ));
+static void pstructdef __P(( definition * ));
+static void puniondef __P(( definition * ));
+static void pprogramdef __P(( definition * ));
+static void pstructdef __P(( definition * ));
+static void penumdef __P(( definition * ));
+static void ptypedef __P(( definition * ));
+static void pdefine __P(( char *, char * ));
+static int undefined2 __P(( char *, char * ));
+static void parglist __P(( proc_list *, char * ));
+static void pprocdef __P(( proc_list *, version_list *, char *, int, int ));
+void pdeclaration __P(( char *, declaration *, int, char * ));
+
+static char RESULT[] = "clnt_res";
+
+
+/*
+ * Print the C-version of an xdr definition
+ */
+void
+print_datadef(def)
+ definition *def;
+{
+
+ if (def->def_kind == DEF_PROGRAM) /* handle data only */
+ return;
+
+ 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) {
+ storexdrfuncdecl(def->def_name,
+ def->def_kind != DEF_TYPEDEF ||
+ !isvectordef(def->def.ty.old_type,
+ def->def.ty.rel));
+ }
+}
+
+
+void
+print_funcdef(def)
+ definition *def;
+{
+ switch (def->def_kind) {
+ case DEF_PROGRAM:
+ f_print(fout, "\n");
+ pprogramdef(def);
+ break;
+ default:
+ }
+}
+
+/* store away enough information to allow the XDR functions to be spat
+ out at the end of the file */
+
+void
+storexdrfuncdecl(name, pointerp)
+char *name;
+int pointerp;
+{
+ xdrfunc * xdrptr;
+
+ xdrptr = (xdrfunc *) malloc(sizeof (struct xdrfunc));
+
+ xdrptr->name = name;
+ xdrptr->pointerp = pointerp;
+ xdrptr->next = NULL;
+
+ if (xdrfunc_tail == NULL){
+ xdrfunc_head = xdrptr;
+ xdrfunc_tail = xdrptr;
+ } else {
+ xdrfunc_tail->next = xdrptr;
+ xdrfunc_tail = xdrptr;
+ }
+
+
+}
+
+void
+print_xdr_func_def(name, pointerp, i)
+char* name;
+int pointerp;
+int i;
+{
+ if (i == 2) {
+ f_print(fout, "extern bool_t xdr_%s();\n", name);
+ return;
+ }
+ else
+ f_print(fout, "extern bool_t xdr_%s(XDR *, %s%s);\n", name,
+ name, pointerp ? "*" : "");
+
+
+}
+
+
+static void
+pconstdef(def)
+ definition *def;
+{
+ pdefine(def->def_name, def->def.co);
+}
+
+/* print out the definitions for the arguments of functions in the
+ header file
+*/
+static void
+pargdef(def)
+ definition *def;
+{
+ decl_list *l;
+ version_list *vers;
+ char *name;
+ proc_list *plist;
+
+
+ for (vers = def->def.pr.versions; vers != NULL; vers = vers->next) {
+ for (plist = vers->procs; plist != NULL;
+ plist = plist->next) {
+
+ if (!newstyle || plist->arg_num < 2) {
+ continue; /* old style or single args */
+ }
+ name = plist->args.argname;
+ f_print(fout, "struct %s {\n", name);
+ for (l = plist->args.decls;
+ l != NULL; l = l->next) {
+ pdeclaration(name, &l->decl, 1,
+ ";\n");
+ }
+ f_print(fout, "};\n");
+ f_print(fout, "typedef struct %s %s;\n",
+ name, name);
+ storexdrfuncdecl(name, 1);
+ f_print(fout, "\n");
+ }
+ }
+}
+
+
+static void
+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, ";\n");
+ }
+ f_print(fout, "};\n");
+ f_print(fout, "typedef struct %s %s;\n", name, name);
+}
+
+static void
+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) {
+ if (l->contflag == 0)
+ pdeclaration(name, &l->case_decl, 2, ";\n");
+ }
+ decl = def->def.un.default_decl;
+ if (decl && !streq(decl->type, "void")) {
+ pdeclaration(name, decl, 2, ";\n");
+ }
+ f_print(fout, "\t} %s_u;\n", name);
+ f_print(fout, "};\n");
+ f_print(fout, "typedef struct %s %s;\n", name, name);
+}
+
+static void
+pdefine(name, num)
+ char *name;
+ char *num;
+{
+ f_print(fout, "#define\t%s %s\n", name, num);
+}
+
+static void
+puldefine(name, num)
+ char *name;
+ char *num;
+{
+ f_print(fout, "#define\t%s ((unsigned long)(%s))\n", name, num);
+}
+
+static int
+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 void
+pfreeprocdef(char * name, char *vers, int mode)
+{
+ f_print(fout, "extern int ");
+ pvname(name, vers);
+ if (mode == 1)
+ f_print(fout,"_freeresult(SVCXPRT *, xdrproc_t, caddr_t);\n");
+ else
+ f_print(fout,"_freeresult();\n");
+
+
+}
+
+static void
+pprogramdef(def)
+ definition *def;
+{
+ version_list *vers;
+ proc_list *proc;
+ int i;
+ char *ext;
+
+ pargdef(def);
+
+ puldefine(def->def_name, def->def.pr.prog_num);
+ for (vers = def->def.pr.versions; vers != NULL; vers = vers->next) {
+ if (tblflag) {
+ f_print(fout,
+ "extern struct rpcgen_table %s_%s_table[];\n",
+ locase(def->def_name), vers->vers_num);
+ f_print(fout,
+ "extern %s_%s_nproc;\n",
+ locase(def->def_name), vers->vers_num);
+ }
+ puldefine(vers->vers_name, vers->vers_num);
+
+ /*
+ * Print out 2 definitions, one for ANSI-C, another for
+ * old K & R C
+ */
+
+ if(!Cflag){
+ ext = "extern ";
+ for (proc = vers->procs; proc != NULL;
+ proc = proc->next) {
+ if (!define_printed(proc,
+ def->def.pr.versions)) {
+ puldefine(proc->proc_name,
+ proc->proc_num);
+ }
+ f_print(fout, "%s", ext);
+ pprocdef(proc, vers, NULL, 0, 2);
+
+ if (mtflag) {
+ f_print(fout, "%s", ext);
+ pprocdef(proc, vers, NULL, 1, 2);
+ }
+ }
+ pfreeprocdef(def->def_name, vers->vers_num, 2);
+
+ } else {
+ for (i = 1; i < 3; i++){
+ if (i == 1){
+ f_print(fout, "\n#if defined(__STDC__) || defined(__cplusplus)\n");
+ ext = "extern ";
+ }else{
+ f_print(fout, "\n#else /* K&R C */\n");
+ ext = "extern ";
+ }
+
+ for (proc = vers->procs; proc != NULL;
+ proc = proc->next) {
+ if (!define_printed(proc,
+ def->def.pr.versions)) {
+ puldefine(proc->proc_name,
+ proc->proc_num);
+ }
+ f_print(fout, "%s", ext);
+ pprocdef(proc, vers, "CLIENT *", 0, i);
+ f_print(fout, "%s", ext);
+ pprocdef(proc, vers, "struct svc_req *", 1, i);
+ }
+ pfreeprocdef(def->def_name, vers->vers_num, i);
+ }
+ f_print(fout, "#endif /* K&R C */\n");
+ }
+ }
+}
+
+static void
+pprocdef(proc, vp, addargtype, server_p, mode)
+ proc_list *proc;
+ version_list *vp;
+ char* addargtype;
+ int server_p;
+ int mode;
+{
+ if (mtflag) {/* Print MT style stubs */
+ if (server_p)
+ f_print(fout, "bool_t ");
+ else
+ f_print(fout, "enum clnt_stat ");
+ } else {
+ ptype(proc->res_prefix, proc->res_type, 1);
+ f_print(fout, "* ");
+ }
+ if (server_p)
+ pvname_svc(proc->proc_name, vp->vers_num);
+ else
+ pvname(proc->proc_name, vp->vers_num);
+
+ /*
+ * mode 1 = ANSI-C, mode 2 = K&R C
+ */
+ if ( mode == 1)
+ parglist(proc, addargtype);
+ else
+ f_print(fout, "();\n");
+}
+
+
+
+/* print out argument list of procedure */
+static void
+parglist(proc, addargtype)
+ proc_list *proc;
+ char* addargtype;
+{
+ decl_list *dl;
+
+ f_print(fout, "(");
+ if (proc->arg_num < 2 && newstyle &&
+ streq(proc->args.decls->decl.type, "void")) {
+ /* 0 argument in new style: do nothing*/
+ }
+ else {
+ for (dl = proc->args.decls; dl != NULL; dl = dl->next) {
+ ptype(dl->decl.prefix, dl->decl.type, 1);
+ if (!newstyle)
+ f_print(fout, "*");
+ /* old style passes by reference */
+ f_print(fout, ", ");
+ }
+ }
+
+ if (mtflag) {
+ ptype(proc->res_prefix, proc->res_type, 1);
+ f_print(fout, "*, ");
+ }
+
+ f_print(fout, "%s);\n", addargtype);
+
+}
+
+static void
+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++);
+ }
+ }
+ if (l->next)
+ f_print(fout, ",\n");
+ else
+ f_print(fout, "\n");
+ }
+ f_print(fout, "};\n");
+ f_print(fout, "typedef enum %s %s;\n", name, name);
+}
+
+static void
+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");
+ }
+}
+
+void
+pdeclaration(name, dec, tab, separator)
+ char *name;
+ declaration *dec;
+ int tab;
+ char *separator;
+{
+ 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, separator);
+}
+
+static int
+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..59431c3
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_main.c
@@ -0,0 +1,1388 @@
+/*
+ * 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
+ */
+
+
+#ident "@(#)rpc_main.c 1.21 94/04/25 SMI"
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_main.c 1.30 89/03/30 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_main.c, Top level of the RPC protocol compiler.
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+#include "rpc_scan.h"
+
+extern void write_sample_svc __P(( definition * ));
+extern int write_sample_clnt __P(( definition * ));
+extern void write_sample_clnt_main __P(( void ));
+extern void add_sample_msg __P(( void ));
+static void c_output __P(( char *, char *, int, char * ));
+static void h_output __P(( char *, char *, int, char * ));
+static void l_output __P(( char *, char *, int, char * ));
+static void t_output __P(( char *, char *, int, char * ));
+static void clnt_output __P(( char *, char *, int, char * ));
+
+void c_initialize __P(( void ));
+
+#ifndef __FreeBSD__
+char * rindex();
+#endif
+
+static void usage __P(( void ));
+static void options_usage __P (( void ));
+static int do_registers __P(( int, char ** ));
+static int parseargs __P(( int, char **, struct commandline * ));
+static void svc_output __P(( char *, char *, int, char * ));
+static void mkfile_output __P(( struct commandline * ));
+static void s_output __P(( int, char **, char *, char *, int, char *, int, int ));
+
+#define EXTEND 1 /* alias for TRUE */
+#define DONT_EXTEND 0 /* alias for FALSE */
+
+#define SVR4_CPP "/usr/ccs/lib/cpp"
+#ifdef __FreeBSD__
+#define SUNOS_CPP "/usr/libexec/cpp"
+#else
+#define SUNOS_CPP "/usr/lib/cpp"
+#endif
+
+static int cppDefined = 0; /* explicit path for C preprocessor */
+
+
+static char *cmdname;
+
+static char *svcclosetime = "120";
+static char *CPP = SVR4_CPP;
+static char CPPFLAGS[] = "-C";
+static char pathbuf[MAXPATHLEN + 1];
+static char *allv[] = {
+ "rpcgen", "-s", "udp", "-s", "tcp",
+};
+static int allc = sizeof (allv)/sizeof (allv[0]);
+static char *allnv[] = {
+ "rpcgen", "-s", "netpath",
+};
+static int allnc = sizeof (allnv)/sizeof (allnv[0]);
+
+/*
+ * machinations for handling expanding argument list
+ */
+static void addarg(); /* add another argument to the list */
+static void putarg(); /* put argument at specified location */
+static void clear_args(); /* clear argument list */
+static void checkfiles(); /* check if out file already exists */
+
+
+
+#define ARGLISTLEN 20
+#define FIXEDARGS 2
+
+static char *arglist[ARGLISTLEN];
+static int argcount = FIXEDARGS;
+
+
+int nonfatalerrors; /* errors */
+#ifdef __FreeBSD__
+int inetdflag = 0; /* Support for inetd is now the default */
+#else
+int inetdflag; /* Support for inetd is now the default */
+#endif
+int pmflag; /* Support for port monitors */
+int logflag; /* Use syslog instead of fprintf for errors */
+int tblflag; /* Support for dispatch table file */
+int mtflag = 0; /* Support for MT */
+#ifdef __FreeBSD__
+#define INLINE 0
+#else
+#define INLINE 5
+#endif
+/* length at which to start doing an inline */
+
+int inline = INLINE;
+/*
+ * Length at which to start doing an inline. INLINE = default
+ * if 0, no xdr_inline code
+ */
+
+int indefinitewait; /* If started by port monitors, hang till it wants */
+int exitnow; /* If started by port monitors, exit after the call */
+int timerflag; /* TRUE if !indefinite && !exitnow */
+int newstyle; /* newstyle of passing arguments (by value) */
+int Cflag = 0; /* ANSI C syntax */
+int CCflag = 0; /* C++ files */
+static int allfiles; /* generate all files */
+#ifdef __FreeBSD__
+int tirpcflag = 0; /* generating code for tirpc, by default */
+#else
+int tirpcflag = 1; /* generating code for tirpc, by default */
+#endif
+xdrfunc *xdrfunc_head = NULL; /* xdr function list */
+xdrfunc *xdrfunc_tail = NULL; /* xdr function list */
+pid_t childpid;
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct commandline cmd;
+
+ (void) memset((char *)&cmd, 0, sizeof (struct commandline));
+ clear_args();
+ if (!parseargs(argc, argv, &cmd))
+ usage();
+ /*
+ * Only the client and server side stubs are likely to be customized,
+ * so in that case only, check if the outfile exists, and if so,
+ * print an error message and exit.
+ */
+ if (cmd.Ssflag || cmd.Scflag || cmd.makefileflag) {
+ checkfiles(cmd.infile, cmd.outfile);
+ }
+ else
+ checkfiles(cmd.infile, NULL);
+
+ if (cmd.cflag) {
+ c_output(cmd.infile, "-DRPC_XDR", DONT_EXTEND, cmd.outfile);
+ } else if (cmd.hflag) {
+ h_output(cmd.infile, "-DRPC_HDR", DONT_EXTEND, cmd.outfile);
+ } else if (cmd.lflag) {
+ l_output(cmd.infile, "-DRPC_CLNT", DONT_EXTEND, cmd.outfile);
+ } else if (cmd.sflag || cmd.mflag || (cmd.nflag)) {
+ s_output(argc, argv, cmd.infile, "-DRPC_SVC", DONT_EXTEND,
+ cmd.outfile, cmd.mflag, cmd.nflag);
+ } else if (cmd.tflag) {
+ t_output(cmd.infile, "-DRPC_TBL", DONT_EXTEND, cmd.outfile);
+ } else if (cmd.Ssflag) {
+ svc_output(cmd.infile, "-DRPC_SERVER", DONT_EXTEND,
+ cmd.outfile);
+ } else if (cmd.Scflag) {
+ clnt_output(cmd.infile, "-DRPC_CLIENT", DONT_EXTEND,
+ cmd.outfile);
+ } else if (cmd.makefileflag) {
+ mkfile_output(&cmd);
+ } else {
+ /* the rescans are required, since cpp may effect input */
+ 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();
+ if (inetdflag || !tirpcflag)
+ s_output(allc, allv, cmd.infile, "-DRPC_SVC", EXTEND,
+ "_svc.c", cmd.mflag, cmd.nflag);
+ else
+ s_output(allnc, allnv, cmd.infile, "-DRPC_SVC",
+ EXTEND, "_svc.c", cmd.mflag, cmd.nflag);
+ if (tblflag) {
+ reinitialize();
+ t_output(cmd.infile, "-DRPC_TBL", EXTEND, "_tbl.i");
+ }
+
+ if (allfiles) {
+ reinitialize();
+ svc_output(cmd.infile, "-DRPC_SERVER", EXTEND,
+ "_server.c");
+ reinitialize();
+ clnt_output(cmd.infile, "-DRPC_CLIENT", EXTEND,
+ "_client.c");
+
+ }
+ if (allfiles || (cmd.makefileflag == 1)){
+ reinitialize();
+ mkfile_output(&cmd);
+ }
+
+ }
+ exit(nonfatalerrors);
+ /* NOTREACHED */
+}
+
+
+/*
+ * add extension to filename
+ */
+static char *
+#ifdef __FreeBSD__
+extendfile(path, ext)
+ char *path;
+#else
+extendfile(file, ext)
+ char *file;
+#endif
+ char *ext;
+{
+ char *res;
+ char *p;
+#ifdef __FreeBSD__
+ char *file;
+
+ if ((file = rindex(path, '/')) == NULL)
+ file = path;
+ else
+ file++;
+#endif
+ res = alloc(strlen(file) + strlen(ext) + 1);
+ if (res == NULL) {
+ abort();
+ }
+ p = strrchr(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 void
+open_output(infile, outfile)
+ char *infile;
+ char *outfile;
+{
+
+ if (outfile == NULL) {
+ fout = stdout;
+ return;
+ }
+
+ if (infile != NULL && streq(outfile, infile)) {
+ f_print(stderr, "%s: %s already exists. No output generated.\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);
+
+ return;
+}
+
+static void
+add_warning()
+{
+ f_print(fout, "/*\n");
+ f_print(fout, " * Please do not edit this file.\n");
+ f_print(fout, " * It was generated using rpcgen.\n");
+ f_print(fout, " */\n\n");
+}
+
+/* clear list of arguments */
+static void clear_args()
+{
+ int i;
+ for (i = FIXEDARGS; i < ARGLISTLEN; i++)
+ arglist[i] = NULL;
+ argcount = FIXEDARGS;
+}
+
+/* make sure that a CPP exists */
+static void find_cpp()
+{
+ struct stat buf;
+
+ if (stat(CPP, &buf) < 0) { /* SVR4 or explicit cpp does not exist */
+ if (cppDefined) {
+ fprintf(stderr,
+ "cannot find C preprocessor: %s \n", CPP);
+ crash();
+ } else { /* try the other one */
+ CPP = SUNOS_CPP;
+ if (stat(CPP, &buf) < 0) { /* can't find any cpp */
+ fprintf(stderr,
+ "cannot find any C preprocessor (cpp)\n");
+ crash();
+ }
+ }
+ }
+}
+
+/*
+ * Open input file with given define for C-preprocessor
+ */
+static void
+open_input(infile, define)
+ char *infile;
+ char *define;
+{
+ int pd[2];
+
+ infilename = (infile == NULL) ? "<stdin>" : infile;
+ (void) pipe(pd);
+ switch (childpid = fork()) {
+ case 0:
+ find_cpp();
+ putarg(0, CPP);
+ putarg(1, CPPFLAGS);
+ addarg(define);
+ if (infile)
+ addarg(infile);
+ addarg((char *)NULL);
+ (void) close(1);
+ (void) dup2(pd[1], 1);
+ (void) close(pd[0]);
+ execv(arglist[0], arglist);
+ perror("execv");
+ 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();
+ }
+}
+
+/* valid tirpc nettypes */
+static char* valid_ti_nettypes[] =
+{
+ "netpath",
+ "visible",
+ "circuit_v",
+ "datagram_v",
+ "circuit_n",
+ "datagram_n",
+ "udp",
+ "tcp",
+ "raw",
+ NULL
+ };
+
+/* valid inetd nettypes */
+static char* valid_i_nettypes[] =
+{
+ "udp",
+ "tcp",
+ NULL
+ };
+
+static int check_nettype(name, list_to_check)
+char* name;
+char* list_to_check[];
+{
+ int i;
+ for (i = 0; list_to_check[i] != NULL; i++) {
+ if (strcmp(name, list_to_check[i]) == 0) {
+ return (1);
+ }
+ }
+ f_print(stderr, "illegal nettype :\'%s\'\n", name);
+ return (0);
+}
+
+static char *
+file_name(file, ext)
+char *file;
+char *ext;
+{
+ char *temp;
+ temp = extendfile(file, ext);
+
+ if (access(temp, F_OK) != -1)
+ return (temp);
+ else
+ return ((char *)" ");
+
+}
+
+
+static void
+c_output(infile, define, extend, outfile)
+ char *infile;
+ char *define;
+ int extend;
+ char *outfile;
+{
+ definition *def;
+ char *include;
+ char *outfilename;
+ long tell;
+
+ c_initialize();
+ open_input(infile, define);
+ outfilename = extend ? extendfile(infile, outfile) : outfile;
+ open_output(infile, outfilename);
+ add_warning();
+ if (infile && (include = extendfile(infile, ".h"))) {
+ f_print(fout, "#include \"%s\"\n", include);
+ free(include);
+ /* .h file already contains rpc/rpc.h */
+ } else
+ f_print(fout, "#include <rpc/rpc.h>\n");
+ tell = ftell(fout);
+ while ( (def = get_definition()) ) {
+ emit(def);
+ }
+ if (extend && tell == ftell(fout)) {
+ (void) unlink(outfilename);
+ }
+}
+
+
+void
+c_initialize()
+{
+
+ /* add all the starting basic types */
+ add_type(1, "int");
+ add_type(1, "long");
+ add_type(1, "short");
+ add_type(1, "bool");
+ add_type(1, "u_int");
+ add_type(1, "u_long");
+ add_type(1, "u_short");
+
+}
+
+char rpcgen_table_dcl[] = "struct rpcgen_table {\n\
+ char *(*proc)(); \n\
+ xdrproc_t xdr_arg; \n\
+ unsigned len_arg; \n\
+ xdrproc_t xdr_res; \n\
+ unsigned len_res; \n\
+}; \n";
+
+
+char *generate_guard(pathname)
+ char* pathname;
+{
+ char* filename, *guard, *tmp;
+
+ filename = strrchr(pathname, '/'); /* find last component */
+ filename = ((filename == 0) ? pathname : filename+1);
+ guard = strdup(filename);
+ /* convert to upper case */
+ tmp = guard;
+ while (*tmp) {
+ if (islower(*tmp))
+ *tmp = toupper(*tmp);
+ tmp++;
+ }
+ guard = extendfile(guard, "_H_RPCGEN");
+ return (guard);
+}
+
+/*
+ * Compile into an XDR header file
+ */
+
+
+static void
+h_output(infile, define, extend, outfile)
+ char *infile;
+ char *define;
+ int extend;
+ char *outfile;
+{
+ definition *def;
+ char *outfilename;
+ long tell;
+ char *guard;
+ list *l;
+ xdrfunc *xdrfuncp;
+ int i;
+
+ open_input(infile, define);
+ outfilename = extend ? extendfile(infile, outfile) : outfile;
+ open_output(infile, outfilename);
+ add_warning();
+ if (outfilename || infile){
+ guard = generate_guard(outfilename ? outfilename: infile);
+ } else
+ guard = "STDIN_";
+
+ f_print(fout, "#ifndef _%s\n#define _%s\n\n", guard,
+ guard);
+
+ f_print(fout, "#include <rpc/rpc.h>\n");
+
+ if (mtflag) {
+ f_print(fout, "#include <synch.h>\n");
+ f_print(fout, "#include <thread.h>\n");
+ };
+
+ /* put the C++ support */
+ if (Cflag && !CCflag){
+ f_print(fout, "\n#ifdef __cplusplus\n");
+ f_print(fout, "extern \"C\" {\n");
+ f_print(fout, "#endif\n\n");
+ }
+
+ /* put in a typedef for quadprecision. Only with Cflag */
+
+ tell = ftell(fout);
+
+ /* print data definitions */
+ while ( (def = get_definition()) ) {
+ print_datadef(def);
+ }
+
+ /*
+ * print function declarations.
+ * Do this after data definitions because they might be used as
+ * arguments for functions
+ */
+ for (l = defined; l != NULL; l = l->next) {
+ print_funcdef(l->val);
+ }
+ /* Now print all xdr func declarations */
+ if (xdrfunc_head != NULL){
+
+ f_print(fout,
+ "\n/* the xdr functions */\n");
+
+ if (CCflag){
+ f_print(fout, "\n#ifdef __cplusplus\n");
+ f_print(fout, "extern \"C\" {\n");
+ f_print(fout, "#endif\n");
+ }
+
+ if (!Cflag){
+ xdrfuncp = xdrfunc_head;
+ while (xdrfuncp != NULL){
+ print_xdr_func_def(xdrfuncp->name,
+ xdrfuncp->pointerp, 2);
+ xdrfuncp = xdrfuncp->next;
+ }
+ } else {
+
+ for (i = 1; i < 3; i++){
+ if (i == 1)
+ f_print(fout, "\n#if defined(__STDC__) || defined(__cplusplus)\n");
+
+ else
+ f_print(fout, "\n#else /* K&R C */\n");
+
+ xdrfuncp = xdrfunc_head;
+ while (xdrfuncp != NULL){
+ print_xdr_func_def(xdrfuncp->name,
+ xdrfuncp->pointerp, i);
+ xdrfuncp = xdrfuncp->next;
+ }
+ }
+ f_print(fout, "\n#endif /* K&R C */\n");
+ }
+ }
+
+ if (extend && tell == ftell(fout)) {
+ (void) unlink(outfilename);
+ } else if (tblflag) {
+ f_print(fout, rpcgen_table_dcl);
+ }
+
+ if (Cflag){
+ f_print(fout, "\n#ifdef __cplusplus\n");
+ f_print(fout, "}\n");
+ f_print(fout, "#endif\n");
+ }
+
+ f_print(fout, "\n#endif /* !_%s */\n", guard);
+}
+
+/*
+ * Compile into an RPC service
+ */
+static void
+s_output(argc, argv, infile, define, extend, outfile, nomain, netflag)
+ int argc;
+ char *argv[];
+ char *infile;
+ char *define;
+ int extend;
+ char *outfile;
+ int nomain;
+ int netflag;
+{
+ char *include;
+ definition *def;
+ int foundprogram = 0;
+ char *outfilename;
+
+ open_input(infile, define);
+ outfilename = extend ? extendfile(infile, outfile) : outfile;
+ open_output(infile, outfilename);
+ add_warning();
+ if (infile && (include = extendfile(infile, ".h"))) {
+ f_print(fout, "#include \"%s\"\n", include);
+ free(include);
+ } else
+ f_print(fout, "#include <rpc/rpc.h>\n");
+
+ f_print(fout, "#include <stdio.h>\n");
+ f_print(fout, "#include <stdlib.h> /* getenv, exit */\n");
+ if (Cflag) {
+ f_print (fout,
+ "#include <rpc/pmap_clnt.h> /* for pmap_unset */\n");
+ f_print (fout, "#include <string.h> /* strcmp */\n");
+ }
+ if (strcmp(svcclosetime, "-1") == 0)
+ indefinitewait = 1;
+ else if (strcmp(svcclosetime, "0") == 0)
+ exitnow = 1;
+ else if (inetdflag || pmflag) {
+ f_print(fout, "#include <signal.h>\n");
+ timerflag = 1;
+ }
+
+ if (!tirpcflag && inetdflag)
+ f_print(fout, "#include <sys/ttycom.h> /* TIOCNOTTY */\n");
+ if (Cflag && (inetdflag || pmflag)) {
+ f_print(fout, "#ifdef __cplusplus\n");
+ f_print(fout,
+ "#include <sysent.h> /* getdtablesize, open */\n");
+ f_print(fout, "#endif /* __cplusplus */\n");
+ if (tirpcflag)
+ f_print(fout, "#include <unistd.h> /* setsid */\n");
+ }
+ if (tirpcflag)
+ f_print(fout, "#include <sys/types.h>\n");
+
+ f_print(fout, "#include <memory.h>\n");
+#ifdef __FreeBSD__
+ if (tirpcflag)
+#endif
+ f_print(fout, "#include <stropts.h>\n");
+ if (inetdflag || !tirpcflag) {
+ f_print(fout, "#include <sys/socket.h>\n");
+ f_print(fout, "#include <netinet/in.h>\n");
+ }
+
+ if ((netflag || pmflag) && tirpcflag && !nomain) {
+ f_print(fout, "#include <netconfig.h>\n");
+ }
+ if (tirpcflag)
+ f_print(fout, "#include <sys/resource.h> /* rlimit */\n");
+ if (logflag || inetdflag || pmflag)
+ f_print(fout, "#include <syslog.h>\n");
+
+ /* for ANSI-C */
+ if (Cflag)
+ f_print(fout,
+ "\n#ifndef SIG_PF\n#define SIG_PF void(*)\
+(int)\n#endif\n");
+
+ f_print(fout, "\n#ifdef DEBUG\n#define RPC_SVC_FG\n#endif\n");
+ if (timerflag)
+ f_print(fout, "\n#define _RPCSVC_CLOSEDOWN %s\n",
+ svcclosetime);
+ while ( (def = get_definition()) ) {
+ foundprogram |= (def->def_kind == DEF_PROGRAM);
+ }
+ if (extend && !foundprogram) {
+ (void) unlink(outfilename);
+ return;
+ }
+ write_most(infile, netflag, nomain);
+ if (!nomain) {
+ if (!do_registers(argc, argv)) {
+ if (outfilename)
+ (void) unlink(outfilename);
+ usage();
+ }
+ write_rest();
+ }
+}
+
+/*
+ * generate client side stubs
+ */
+static void
+l_output(infile, define, extend, outfile)
+ char *infile;
+ char *define;
+ int extend;
+ char *outfile;
+{
+ char *include;
+ definition *def;
+ int foundprogram = 0;
+ char *outfilename;
+
+ open_input(infile, define);
+ outfilename = extend ? extendfile(infile, outfile) : outfile;
+ open_output(infile, outfilename);
+ add_warning();
+ if (Cflag)
+ f_print (fout, "#include <memory.h> /* for memset */\n");
+ if (infile && (include = extendfile(infile, ".h"))) {
+ f_print(fout, "#include \"%s\"\n", include);
+ free(include);
+ } else
+ f_print(fout, "#include <rpc/rpc.h>\n");
+ while ( (def = get_definition()) ) {
+ foundprogram |= (def->def_kind == DEF_PROGRAM);
+ }
+ if (extend && !foundprogram) {
+ (void) unlink(outfilename);
+ return;
+ }
+ write_stubs();
+}
+
+/*
+ * generate the dispatch table
+ */
+static void
+t_output(infile, define, extend, outfile)
+ char *infile;
+ char *define;
+ int extend;
+ char *outfile;
+{
+ definition *def;
+ int foundprogram = 0;
+ char *outfilename;
+
+ open_input(infile, define);
+ outfilename = extend ? extendfile(infile, outfile) : outfile;
+ open_output(infile, outfilename);
+ add_warning();
+ while ( (def = get_definition()) ) {
+ foundprogram |= (def->def_kind == DEF_PROGRAM);
+ }
+ if (extend && !foundprogram) {
+ (void) unlink(outfilename);
+ return;
+ }
+ write_tables();
+}
+
+/* sample routine for the server template */
+static void
+svc_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;
+ checkfiles(infile, outfilename);
+ /*
+ * Check if outfile already exists.
+ * if so, print an error message and exit
+ */
+ open_output(infile, outfilename);
+ add_sample_msg();
+
+ if (infile && (include = extendfile(infile, ".h"))) {
+ f_print(fout, "#include \"%s\"\n", include);
+ free(include);
+ } else
+ f_print(fout, "#include <rpc/rpc.h>\n");
+
+ tell = ftell(fout);
+ while ( (def = get_definition()) ) {
+ write_sample_svc(def);
+ }
+ if (extend && tell == ftell(fout)) {
+ (void) unlink(outfilename);
+ }
+}
+
+/* sample main routine for client */
+static void
+clnt_output(infile, define, extend, outfile)
+ char *infile;
+ char *define;
+ int extend;
+ char *outfile;
+{
+ definition *def;
+ char *include;
+ char *outfilename;
+ long tell;
+ int has_program = 0;
+
+ open_input(infile, define);
+ outfilename = extend ? extendfile(infile, outfile) : outfile;
+ checkfiles(infile, outfilename);
+ /*
+ * Check if outfile already exists.
+ * if so, print an error message and exit
+ */
+
+ open_output(infile, outfilename);
+ add_sample_msg();
+ if (infile && (include = extendfile(infile, ".h"))) {
+ f_print(fout, "#include \"%s\"\n", include);
+ free(include);
+ } else
+ f_print(fout, "#include <rpc/rpc.h>\n");
+ tell = ftell(fout);
+ while ( (def = get_definition()) ) {
+ has_program += write_sample_clnt(def);
+ }
+
+ if (has_program)
+ write_sample_clnt_main();
+
+ if (extend && tell == ftell(fout)) {
+ (void) unlink(outfilename);
+ }
+}
+
+
+static void mkfile_output(cmd)
+struct commandline *cmd;
+{
+ char *mkfilename, *clientname, *clntname, *xdrname, *hdrname;
+ char *servername, *svcname, *servprogname, *clntprogname;
+ char *temp;
+
+ svcname = file_name(cmd->infile, "_svc.c");
+ clntname = file_name(cmd->infile, "_clnt.c");
+ xdrname = file_name(cmd->infile, "_xdr.c");
+ hdrname = file_name(cmd->infile, ".h");
+
+
+ if (allfiles){
+ servername = extendfile(cmd->infile, "_server.c");
+ clientname = extendfile(cmd->infile, "_client.c");
+ }else{
+ servername = " ";
+ clientname = " ";
+ }
+ servprogname = extendfile(cmd->infile, "_server");
+ clntprogname = extendfile(cmd->infile, "_client");
+
+ if (allfiles){
+ mkfilename = alloc(strlen("makefile.") +
+ strlen(cmd->infile) + 1);
+ temp = (char *)rindex(cmd->infile, '.');
+ strcat(mkfilename, "makefile.");
+ (void) strncat(mkfilename, cmd->infile,
+ (temp - cmd->infile));
+ } else
+ mkfilename = cmd->outfile;
+
+
+ checkfiles(NULL, mkfilename);
+ open_output(NULL, mkfilename);
+
+ f_print(fout, "\n# This is a template makefile generated\
+ by rpcgen \n");
+
+ f_print(fout, "\n# Parameters \n\n");
+
+ f_print(fout, "CLIENT = %s\nSERVER = %s\n\n",
+ clntprogname, servprogname);
+ f_print(fout, "SOURCES_CLNT.c = \nSOURCES_CLNT.h = \n");
+ f_print(fout, "SOURCES_SVC.c = \nSOURCES_SVC.h = \n");
+ f_print(fout, "SOURCES.x = %s\n\n", cmd->infile);
+ f_print(fout, "TARGETS_SVC.c = %s %s %s \n",
+ svcname, servername, xdrname);
+ f_print(fout, "TARGETS_CLNT.c = %s %s %s \n",
+ clntname, clientname, xdrname);
+ f_print(fout, "TARGETS = %s %s %s %s %s %s\n\n",
+ hdrname, xdrname, clntname,
+ svcname, clientname, servername);
+
+ f_print(fout, "OBJECTS_CLNT = $(SOURCES_CLNT.c:%%.c=%%.o) \
+$(TARGETS_CLNT.c:%%.c=%%.o) ");
+
+ f_print(fout, "\nOBJECTS_SVC = $(SOURCES_SVC.c:%%.c=%%.o) \
+$(TARGETS_SVC.c:%%.c=%%.o) ");
+
+
+ f_print(fout, "\n# Compiler flags \n");
+ if (mtflag)
+ f_print(fout, "\nCPPFLAGS += -D_REENTRANT\nCFLAGS += -g \nLDLIBS += -lnsl -lthread\n");
+ else
+#ifdef __FreeBSD__
+ f_print(fout, "\nCFLAGS += -g \nLDLIBS +=\n");
+#else
+ f_print(fout, "\nCFLAGS += -g \nLDLIBS += -lnsl\n");
+#endif
+ f_print(fout, "RPCGENFLAGS = \n");
+
+ f_print(fout, "\n# Targets \n\n");
+
+ f_print(fout, "all : $(CLIENT) $(SERVER)\n\n");
+ f_print(fout, "$(TARGETS) : $(SOURCES.x) \n");
+ f_print(fout, "\trpcgen $(RPCGENFLAGS) $(SOURCES.x)\n\n");
+ f_print(fout, "$(OBJECTS_CLNT) : $(SOURCES_CLNT.c) $(SOURCES_CLNT.h) \
+$(TARGETS_CLNT.c) \n\n");
+
+ f_print(fout, "$(OBJECTS_SVC) : $(SOURCES_SVC.c) $(SOURCES_SVC.h) \
+$(TARGETS_SVC.c) \n\n");
+ f_print(fout, "$(CLIENT) : $(OBJECTS_CLNT) \n");
+#ifdef __FreeBSD__
+ f_print(fout, "\t$(CC) -o $(CLIENT) $(OBJECTS_CLNT) \
+$(LDLIBS) \n\n");
+#else
+ f_print(fout, "\t$(LINK.c) -o $(CLIENT) $(OBJECTS_CLNT) \
+$(LDLIBS) \n\n");
+#endif
+ f_print(fout, "$(SERVER) : $(OBJECTS_SVC) \n");
+#ifdef __FreeBSD__
+ f_print(fout, "\t$(CC) -o $(SERVER) $(OBJECTS_SVC) $(LDLIBS)\n\n ");
+ f_print(fout, "clean:\n\t $(RM) -f core $(TARGETS) $(OBJECTS_CLNT) \
+$(OBJECTS_SVC) $(CLIENT) $(SERVER)\n\n");
+#else
+ f_print(fout, "\t$(LINK.c) -o $(SERVER) $(OBJECTS_SVC) $(LDLIBS)\n\n ");
+ f_print(fout, "clean:\n\t $(RM) core $(TARGETS) $(OBJECTS_CLNT) \
+$(OBJECTS_SVC) $(CLIENT) $(SERVER)\n\n");
+#endif
+}
+
+
+
+/*
+ * Perform registrations for service output
+ * Return 0 if failed; 1 otherwise.
+ */
+static int
+do_registers(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+
+ if (inetdflag || !tirpcflag) {
+ for (i = 1; i < argc; i++) {
+ if (streq(argv[i], "-s")) {
+ if (!check_nettype(argv[i + 1],
+ valid_i_nettypes))
+ return (0);
+ write_inetd_register(argv[i + 1]);
+ i++;
+ }
+ }
+ } else {
+ for (i = 1; i < argc; i++)
+ if (streq(argv[i], "-s")) {
+ if (!check_nettype(argv[i + 1],
+ valid_ti_nettypes))
+ return (0);
+ write_nettype_register(argv[i + 1]);
+ i++;
+ } else if (streq(argv[i], "-n")) {
+ write_netid_register(argv[i + 1]);
+ i++;
+ }
+ }
+ return (1);
+}
+
+/*
+ * Add another argument to the arg list
+ */
+static void
+addarg(cp)
+ char *cp;
+{
+ if (argcount >= ARGLISTLEN) {
+ f_print(stderr, "rpcgen: too many defines\n");
+ crash();
+ /*NOTREACHED*/
+ }
+ arglist[argcount++] = cp;
+
+}
+
+static void
+putarg(where, cp)
+ char *cp;
+ int where;
+{
+ if (where >= ARGLISTLEN) {
+ f_print(stderr, "rpcgen: arglist coding error\n");
+ crash();
+ /*NOTREACHED*/
+ }
+ arglist[where] = cp;
+}
+
+/*
+ * if input file is stdin and an output file is specified then complain
+ * if the file already exists. Otherwise the file may get overwritten
+ * If input file does not exist, exit with an error
+ */
+
+static void
+checkfiles(infile, outfile)
+char *infile;
+char *outfile;
+{
+
+ struct stat buf;
+
+ if (infile) /* infile ! = NULL */
+ if (stat(infile, &buf) < 0)
+ {
+ perror(infile);
+ crash();
+ };
+ if (outfile) {
+ if (stat(outfile, &buf) < 0)
+ return; /* file does not exist */
+ else {
+ f_print(stderr,
+ "file '%s' already exists and may be overwritten\n",
+ outfile);
+ crash();
+ }
+ }
+}
+
+/*
+ * Parse command line arguments
+ */
+static int
+parseargs(argc, argv, cmd)
+ int argc;
+ char *argv[];
+ struct commandline *cmd;
+{
+ int i;
+ int j;
+ char c, ch;
+ char flag[(1 << 8 * sizeof (char))];
+ int nflags;
+
+ cmdname = argv[0];
+ cmd->infile = cmd->outfile = NULL;
+ if (argc < 2) {
+ return (0);
+ }
+ allfiles = 0;
+ flag['c'] = 0;
+ flag['h'] = 0;
+ flag['l'] = 0;
+ flag['m'] = 0;
+ flag['o'] = 0;
+ flag['s'] = 0;
+ flag['n'] = 0;
+ flag['t'] = 0;
+ flag['S'] = 0;
+ flag['C'] = 0;
+ flag['M'] = 0;
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] != '-') {
+ if (cmd->infile) {
+ f_print(stderr,
+ "Cannot specify more than one input file.\n");
+
+ return (0);
+ }
+ cmd->infile = argv[i];
+ } else {
+ for (j = 1; argv[i][j] != 0; j++) {
+ c = argv[i][j];
+ switch (c) {
+ case 'a':
+ allfiles = 1;
+ break;
+ case 'c':
+ case 'h':
+ case 'l':
+ case 'm':
+ case 't':
+ if (flag[(int)c]) {
+ return (0);
+ }
+ flag[(int)c] = 1;
+ break;
+ case 'S':
+ /*
+ * sample flag: Ss or Sc.
+ * Ss means set flag['S'];
+ * Sc means set flag['C'];
+ * Sm means set flag['M'];
+ */
+ ch = argv[i][++j]; /* get next char */
+ if (ch == 's')
+ ch = 'S';
+ else if (ch == 'c')
+ ch = 'C';
+ else if (ch == 'm')
+ ch = 'M';
+ else
+ return (0);
+
+ if (flag[(int)ch]) {
+ return (0);
+ }
+ flag[(int)ch] = 1;
+ break;
+ case 'C': /* ANSI C syntax */
+ Cflag = 1;
+ ch = argv[i][j+1]; /* get next char */
+
+ if (ch != 'C')
+ break;
+ CCflag = 1;
+ break;
+ case 'b':
+ /*
+ * Turn TIRPC flag off for
+ * generating backward compatible
+ * code
+ */
+#ifdef __FreeBSD__
+ tirpcflag = 1;
+#else
+ tirpcflag = 0;
+#endif
+ break;
+
+ case 'I':
+ inetdflag = 1;
+ break;
+ case 'N':
+ newstyle = 1;
+ break;
+ case 'L':
+ logflag = 1;
+ break;
+ case 'K':
+ if (++i == argc) {
+ return (0);
+ }
+ svcclosetime = argv[i];
+ goto nextarg;
+ case 'T':
+ tblflag = 1;
+ break;
+ case 'M':
+ mtflag = 1;
+ break;
+ case 'i' :
+ if (++i == argc) {
+ return (0);
+ }
+ inline = atoi(argv[i]);
+ goto nextarg;
+ case 'n':
+ case 'o':
+ case 's':
+ if (argv[i][j - 1] != '-' ||
+ argv[i][j + 1] != 0) {
+ return (0);
+ }
+ flag[(int)c] = 1;
+ if (++i == argc) {
+ return (0);
+ }
+ if (c == 'o') {
+ if (cmd->outfile) {
+ return (0);
+ }
+ cmd->outfile = argv[i];
+ }
+ goto nextarg;
+ case 'D':
+ if (argv[i][j - 1] != '-') {
+ return (0);
+ }
+ (void) addarg(argv[i]);
+ goto nextarg;
+ case 'Y':
+ if (++i == argc) {
+ return (0);
+ }
+ (void) strcpy(pathbuf, argv[i]);
+ (void) strcat(pathbuf, "/cpp");
+ CPP = pathbuf;
+ cppDefined = 1;
+ goto nextarg;
+
+
+
+ default:
+ return (0);
+ }
+ }
+ nextarg:
+ ;
+ }
+ }
+
+ cmd->cflag = flag['c'];
+ cmd->hflag = flag['h'];
+ cmd->lflag = flag['l'];
+ cmd->mflag = flag['m'];
+ cmd->nflag = flag['n'];
+ cmd->sflag = flag['s'];
+ cmd->tflag = flag['t'];
+ cmd->Ssflag = flag['S'];
+ cmd->Scflag = flag['C'];
+ cmd->makefileflag = flag['M'];
+
+ if (tirpcflag) {
+ pmflag = inetdflag ? 0 : 1;
+ /* pmflag or inetdflag is always TRUE */
+ if ((inetdflag && cmd->nflag)) {
+ /* netid not allowed with inetdflag */
+ f_print(stderr, "Cannot use netid flag with inetd flag.\n");
+ return (0);
+ }
+ } else { /* 4.1 mode */
+ pmflag = 0; /* set pmflag only in tirpcmode */
+#ifndef __FreeBSD__
+ inetdflag = 1; /* inetdflag is TRUE by default */
+#endif
+ if (cmd->nflag) { /* netid needs TIRPC */
+ f_print(stderr, "Cannot use netid flag without TIRPC.\n");
+ return (0);
+ }
+ }
+
+ if (newstyle && (tblflag || cmd->tflag)) {
+ f_print(stderr, "Cannot use table flags with newstyle.\n");
+ return (0);
+ }
+
+ /* check no conflicts with file generation flags */
+ nflags = cmd->cflag + cmd->hflag + cmd->lflag + cmd->mflag +
+ cmd->sflag + cmd->nflag + cmd->tflag + cmd->Ssflag +
+ cmd->Scflag + cmd->makefileflag;
+
+ if (nflags == 0) {
+ if (cmd->outfile != NULL || cmd->infile == NULL) {
+ return (0);
+ }
+ } else if (cmd->infile == NULL &&
+ (cmd->Ssflag || cmd->Scflag || cmd->makefileflag)) {
+ f_print(stderr,
+ "\"infile\" is required for template generation flags.\n");
+ return (0);
+ } if (nflags > 1) {
+ f_print(stderr,
+ "Cannot have more than one file generation flag.\n");
+ return (0);
+ }
+ return (1);
+}
+
+static void
+usage()
+{
+ f_print(stderr, "usage: %s infile\n", cmdname);
+ f_print(stderr,
+ "\t%s [-abCLNTM] [-Dname[=value]] [-i size]\
+[-I [-K seconds]] [-Y path] infile\n",
+ cmdname);
+ f_print(stderr,
+ "\t%s [-c | -h | -l | -m | -t | -Sc | -Ss | -Sm]\
+[-o outfile] [infile]\n",
+ cmdname);
+ f_print(stderr, "\t%s [-s nettype]* [-o outfile] [infile]\n", cmdname);
+ f_print(stderr, "\t%s [-n netid]* [-o outfile] [infile]\n", cmdname);
+ options_usage();
+ exit(1);
+}
+
+static void
+options_usage()
+{
+ f_print(stderr, "options:\n");
+ f_print(stderr, "-a\t\tgenerate all files, including samples\n");
+ f_print(stderr, "-b\t\tbackward compatibility mode (generates code\
+for SunOS 4.X)\n");
+ f_print(stderr, "-c\t\tgenerate XDR routines\n");
+ f_print(stderr, "-C\t\tANSI C mode\n");
+ f_print(stderr, "-Dname[=value]\tdefine a symbol (same as #define)\n");
+ f_print(stderr, "-h\t\tgenerate header file\n");
+ f_print(stderr, "-i size\t\tsize at which to start generating\
+inline code\n");
+ f_print(stderr, "-I\t\tgenerate code for inetd support in server\
+(for SunOS 4.X)\n");
+ f_print(stderr, "-K seconds\tserver exits after K seconds of\
+inactivity\n");
+ f_print(stderr, "-l\t\tgenerate client side stubs\n");
+ f_print(stderr, "-L\t\tserver errors will be printed to syslog\n");
+ f_print(stderr, "-m\t\tgenerate server side stubs\n");
+ f_print(stderr, "-M\t\tgenerate MT-safe code\n");
+ f_print(stderr, "-n netid\tgenerate server code that supports\
+named netid\n");
+ f_print(stderr, "-N\t\tsupports multiple arguments and\
+call-by-value\n");
+ f_print(stderr, "-o outfile\tname of the output file\n");
+ f_print(stderr, "-s nettype\tgenerate server code that supports named\
+nettype\n");
+ f_print(stderr, "-Sc\t\tgenerate sample client code that uses remote\
+procedures\n");
+ f_print(stderr, "-Ss\t\tgenerate sample server code that defines\
+remote procedures\n");
+ f_print(stderr, "-Sm \t\tgenerate makefile template \n");
+
+ f_print(stderr, "-t\t\tgenerate RPC dispatch table\n");
+ f_print(stderr, "-T\t\tgenerate code to support RPC dispatch tables\n");
+ f_print(stderr, "-Y path\t\tpath where cpp is found\n");
+ exit(1);
+}
+
+#ifndef __FreeBSD__
+char *
+rindex(sp, c)
+ register char *sp, c;
+{
+ register char *r;
+
+ r = NULL;
+ do {
+ if (*sp == c)
+ r = sp;
+ } while (*sp++);
+ return (r);
+}
+#endif
diff --git a/usr.bin/rpcgen/rpc_parse.c b/usr.bin/rpcgen/rpc_parse.c
new file mode 100644
index 0000000..5431520
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_parse.c
@@ -0,0 +1,656 @@
+/*
+ * 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
+ */
+
+#ident "@(#)rpc_parse.c 1.12 93/07/05 SMI"
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_parse.c 1.8 89/02/22 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_parse.c, Parser for the RPC protocol compiler
+ * Copyright (C) 1987 Sun Microsystems, Inc.
+ */
+#include <stdio.h>
+#include <string.h>
+#include "rpc/types.h"
+#include "rpc_scan.h"
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+#define ARGNAME "arg"
+
+extern char *make_argname __P(( char *, char * ));
+static void isdefined __P(( definition * ));
+static void def_struct __P(( definition * ));
+static void def_program __P(( definition * ));
+static void def_enum __P(( definition * ));
+static void def_const __P(( definition * ));
+static void def_union __P(( definition * ));
+static void def_typedef __P(( definition * ));
+static void get_declaration __P(( declaration *, defkind ));
+static void get_prog_declaration __P(( declaration *, defkind, int ));
+static void get_type __P(( char **, char **, defkind ));
+static void unsigned_dec __P(( char ** ));
+
+#ifndef __FreeBSD__
+extern char *strdup();
+#endif
+
+/*
+ * 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);
+ default:
+ error("definition keyword expected");
+ }
+ scan(TOK_SEMICOLON, &tok);
+ isdefined(defp);
+ return (defp);
+}
+
+static void
+isdefined(defp)
+ definition *defp;
+{
+ STOREVAL(&defined, defp);
+}
+
+static void
+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 void
+def_program(defp)
+ definition *defp;
+{
+ token tok;
+ declaration dec;
+ decl_list *decls;
+ decl_list **tailp;
+ version_list *vlist;
+ version_list **vtailp;
+ proc_list *plist;
+ proc_list **ptailp;
+ int num_args;
+ bool_t isvoid = FALSE; /* whether first argument is void */
+ defp->def_kind = DEF_PROGRAM;
+ scan(TOK_IDENT, &tok);
+ defp->def_name = tok.str;
+ scan(TOK_LBRACE, &tok);
+ vtailp = &defp->def.pr.versions;
+ tailp = &defp->def.st.decls;
+ 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 {
+ /* get result type */
+ 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 args - first one */
+ num_args = 1;
+ isvoid = FALSE;
+ /*
+ * type of DEF_PROGRAM in the first
+ * get_prog_declaration and DEF_STURCT in the next
+ * allows void as argument if it is the only argument
+ */
+ get_prog_declaration(&dec, DEF_PROGRAM, num_args);
+ if (streq(dec.type, "void"))
+ isvoid = TRUE;
+ decls = ALLOC(decl_list);
+ plist->args.decls = decls;
+ decls->decl = dec;
+ tailp = &decls->next;
+ /* get args */
+ while (peekscan(TOK_COMMA, &tok)) {
+ num_args++;
+ get_prog_declaration(&dec, DEF_STRUCT,
+ num_args);
+ decls = ALLOC(decl_list);
+ decls->decl = dec;
+ *tailp = decls;
+ if (streq(dec.type, "void"))
+ isvoid = TRUE;
+ tailp = &decls->next;
+ }
+ /* multiple arguments are only allowed in newstyle */
+ if (!newstyle && num_args > 1) {
+ error("only one argument is allowed");
+ }
+ if (isvoid && num_args > 1) {
+ error("illegal use of void in program definition");
+ }
+ *tailp = NULL;
+ scan(TOK_RPAREN, &tok);
+ scan(TOK_EQUAL, &tok);
+ scan_num(&tok);
+ scan(TOK_SEMICOLON, &tok);
+ plist->proc_num = tok.str;
+ plist->arg_num = num_args;
+ *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;
+ /* make the argument structure name for each arg */
+ for (plist = vlist->procs; plist != NULL;
+ plist = plist->next) {
+ plist->args.argname = make_argname(plist->proc_name,
+ vlist->vers_num);
+ /* free the memory ?? */
+ }
+ 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 void
+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 void
+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 void
+def_union(defp)
+ definition *defp;
+{
+ token tok;
+ declaration dec;
+ case_list *cases;
+ case_list **tailp;
+ int flag;
+
+ 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) {
+ scan2(TOK_IDENT, TOK_CHARCONST, &tok);
+ cases = ALLOC(case_list);
+ cases->case_name = tok.str;
+ scan(TOK_COLON, &tok);
+ /* now peek at next token */
+ flag = 0;
+ if (peekscan(TOK_CASE, &tok)){
+ do {
+ scan2(TOK_IDENT, TOK_CHARCONST, &tok);
+ cases->contflag = 1;
+ /* continued case statement */
+ *tailp = cases;
+ tailp = &cases->next;
+ cases = ALLOC(case_list);
+ cases->case_name = tok.str;
+ scan(TOK_COLON, &tok);
+ } while (peekscan(TOK_CASE, &tok));
+ }
+ else
+ if (flag)
+ {
+
+ *tailp = cases;
+ tailp = &cases->next;
+ cases = ALLOC(case_list);
+ };
+
+ get_declaration(&dec, DEF_UNION);
+ cases->case_decl = dec;
+ cases->contflag = 0; /* no continued case statement */
+ *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 char* reserved_words[] =
+{
+ "array",
+ "bytes",
+ "destroy",
+ "free",
+ "getpos",
+ "inline",
+ "pointer",
+ "reference",
+ "setpos",
+ "sizeof",
+ "union",
+ "vector",
+ NULL
+ };
+
+static char* reserved_types[] =
+{
+ "opaque",
+ "string",
+ NULL
+ };
+
+/*
+ * check that the given name is not one that would eventually result in
+ * xdr routines that would conflict with internal XDR routines.
+ */
+static void
+check_type_name(name, new_type)
+int new_type;
+char* name;
+{
+ int i;
+ char tmp[100];
+
+ for (i = 0; reserved_words[i] != NULL; i++) {
+ if (strcmp(name, reserved_words[i]) == 0) {
+ sprintf(tmp,
+ "illegal (reserved) name :\'%s\' in type definition",
+ name);
+ error(tmp);
+ }
+ }
+ if (new_type) {
+ for (i = 0; reserved_types[i] != NULL; i++) {
+ if (strcmp(name, reserved_types[i]) == 0) {
+ sprintf(tmp,
+ "illegal (reserved) name :\'%s\' in type definition",
+ name);
+ error(tmp);
+ }
+ }
+ }
+}
+
+
+
+static void
+def_typedef(defp)
+ definition *defp;
+{
+ declaration dec;
+
+ defp->def_kind = DEF_TYPEDEF;
+ get_declaration(&dec, DEF_TYPEDEF);
+ defp->def_name = dec.name;
+ check_type_name(dec.name, 1);
+ 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 void
+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;
+ }
+
+ check_type_name(dec->type, 0);
+ 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 void
+get_prog_declaration(dec, dkind, num)
+ declaration *dec;
+ defkind dkind;
+ int num; /* arg number */
+{
+ token tok;
+ char name[10]; /* argument name */
+
+ if (dkind == DEF_PROGRAM) {
+ peek(&tok);
+ if (tok.kind == TOK_RPAREN) { /* no arguments */
+ dec->rel = REL_ALIAS;
+ dec->type = "void";
+ dec->prefix = NULL;
+ dec->name = NULL;
+ return;
+ }
+ }
+ get_type(&dec->prefix, &dec->type, dkind);
+ dec->rel = REL_ALIAS;
+ if (peekscan(TOK_IDENT, &tok)) /* optional name of argument */
+ strcpy(name, tok.str);
+ else
+ sprintf(name, "%s%d", ARGNAME, num);
+ /* default name of argument */
+
+ dec->name = (char *) strdup(name);
+ if (streq(dec->type, "void")) {
+ return;
+ }
+
+ if (streq(dec->type, "opaque")) {
+ error("opaque -- illegal argument type");
+ }
+ if (peekscan(TOK_STAR, &tok)) {
+ if (streq(dec->type, "string")) {
+ error("pointer to string not allowed in program arguments\n");
+ }
+ dec->rel = REL_POINTER;
+ if (peekscan(TOK_IDENT, &tok))
+ /* optional name of argument */
+ dec->name = strdup(tok.str);
+ }
+ if (peekscan(TOK_LANGLE, &tok)) {
+ if (!streq(dec->type, "string")) {
+ error("arrays cannot be declared as arguments to procedures -- 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, "string")) {
+ if (dec->rel != REL_ARRAY) {
+ /*
+ * .x specifies just string as
+ * type of argument
+ * - make it string<>
+ */
+ dec->rel = REL_ARRAY;
+ dec->array_max = "~0"; /* unspecified size, use max */
+ }
+ }
+}
+
+
+
+static void
+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_HYPER:
+ *typep = "longlong_t";
+ (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 with one argument");
+ }
+ *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:
+ case TOK_QUAD:
+ *typep = tok.str;
+ break;
+ default:
+ error("expected type specifier");
+ }
+}
+
+static void
+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_HYPER:
+ get_token(&tok);
+ *typep = "ulonglong_t";
+ (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..b61db9d
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_parse.h
@@ -0,0 +1,197 @@
+/*
+ * 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
+ */
+#pragma ident "@(#)rpc_parse.h 1.10 94/05/15 SMI"
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
+/* The copyright notice above does not evidence any */
+/* actual or intended publication of such source code. */
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+* PROPRIETARY NOTICE (Combined)
+*
+* This source code is unpublished proprietary information
+* constituting, or derived under license from AT&T's UNIX(r) System V.
+* In addition, portions of such source code were derived from Berkeley
+* 4.3 BSD under license from the Regents of the University of
+* California.
+*
+*
+*
+* Copyright Notice
+*
+* Notice of copyright on this source code product does not indicate
+* publication.
+*
+* (c) 1986,1987,1988.1989 Sun Microsystems, Inc
+* (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
+* All rights reserved.
+*/
+
+/* @(#)rpc_parse.h 1.3 90/08/29 (C) 1987 SMI */
+
+/*
+ * rpc_parse.h, Definitions for the RPCL parser
+ */
+
+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;
+ int contflag;
+ 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 arg_list {
+ char *argname; /* name of struct for arg*/
+ decl_list *decls;
+};
+
+typedef struct arg_list arg_list;
+
+struct proc_list {
+ char *proc_name;
+ char *proc_num;
+ arg_list args;
+ int arg_num;
+ 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;
+
+definition *get_definition();
+
+
+struct bas_type
+{
+ char *name;
+ int length;
+ struct bas_type *next;
+};
+
+typedef struct bas_type bas_type;
diff --git a/usr.bin/rpcgen/rpc_sample.c b/usr.bin/rpcgen/rpc_sample.c
new file mode 100644
index 0000000..1de374c
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_sample.c
@@ -0,0 +1,312 @@
+/*
+ * 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
+ */
+
+#pragma ident "@(#)rpc_sample.c 1.9 94/04/25 SMI"
+
+/*
+ * rpc_sample.c, Sample client-server code outputter for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+
+static char RQSTP[] = "rqstp";
+
+extern void printarglist __P(( proc_list *, char *, char *, char *));
+static void write_sample_client __P(( char *, version_list * ));
+static void write_sample_server __P(( definition * ));
+static void return_type __P(( proc_list * ));
+
+void
+write_sample_svc(def)
+ definition *def;
+{
+
+ if (def->def_kind != DEF_PROGRAM)
+ return;
+ write_sample_server(def);
+}
+
+
+int
+write_sample_clnt(def)
+ definition *def;
+{
+ version_list *vp;
+ int count = 0;
+
+ if (def->def_kind != DEF_PROGRAM)
+ return(0);
+ /* generate sample code for each version */
+ for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+ write_sample_client(def->def_name, vp);
+ ++count;
+ }
+ return(count);
+}
+
+
+static void
+write_sample_client(program_name, vp)
+ char *program_name;
+ version_list *vp;
+{
+ proc_list *proc;
+ int i;
+ decl_list *l;
+
+ f_print(fout, "\n\nvoid\n");
+ pvname(program_name, vp->vers_num);
+ if(Cflag)
+ f_print(fout,"(char *host)\n{\n");
+ else
+ f_print(fout, "(host)\n\tchar *host;\n{\n");
+ f_print(fout, "\tCLIENT *clnt;\n");
+
+ i = 0;
+ for (proc = vp->procs; proc != NULL; proc = proc->next) {
+ f_print(fout, "\t");
+ if (mtflag) {
+ f_print(fout, "enum clnt_stat retval_%d;\n\t", ++i);
+ ptype(proc->res_prefix, proc->res_type, 1);
+ f_print(fout, "result_%d;\n", i);
+ } else {
+ ptype(proc->res_prefix, proc->res_type, 1);
+ f_print(fout, " *result_%d;\n",++i);
+ }
+ /* print out declarations for arguments */
+ if(proc->arg_num < 2 && !newstyle) {
+ f_print(fout, "\t");
+ if(!streq(proc->args.decls->decl.type, "void"))
+ ptype(proc->args.decls->decl.prefix,
+ proc->args.decls->decl.type, 1);
+ else
+ f_print(fout, "char * "); /* cannot have "void" type */
+ f_print(fout, " ");
+ pvname(proc->proc_name, vp->vers_num);
+ f_print(fout, "_arg;\n");
+ } else if (!streq(proc->args.decls->decl.type, "void")) {
+ for (l = proc->args.decls; l != NULL; l = l->next) {
+ f_print(fout, "\t");
+ ptype(l->decl.prefix, l->decl.type, 1);
+ if (strcmp(l->decl.type,"string") == 1)
+ f_print(fout, " ");
+ pvname(proc->proc_name, vp->vers_num);
+ f_print(fout, "_%s;\n", l->decl.name);
+ }
+ }
+ }
+
+ /* generate creation of client handle */
+ f_print(fout, "\n#ifndef\tDEBUG\n");
+ f_print(fout, "\tclnt = clnt_create(host, %s, %s, \"%s\");\n",
+ program_name, vp->vers_name, tirpcflag? "netpath" : "udp");
+ f_print(fout, "\tif (clnt == (CLIENT *) NULL) {\n");
+ f_print(fout, "\t\tclnt_pcreateerror(host);\n");
+ f_print(fout, "\t\texit(1);\n\t}\n");
+ f_print(fout, "#endif\t/* DEBUG */\n\n");
+
+ /* generate calls to procedures */
+ i = 0;
+ for (proc = vp->procs; proc != NULL; proc = proc->next) {
+ if (mtflag)
+ f_print(fout, "\tretval_%d = ",++i);
+ else
+ f_print(fout, "\tresult_%d = ",++i);
+ pvname(proc->proc_name, vp->vers_num);
+ if (proc->arg_num < 2 && !newstyle) {
+ f_print(fout, "(");
+ if(streq(proc->args.decls->decl.type, "void"))
+ /* cast to void * */
+ f_print(fout, "(void *)");
+ f_print(fout, "&");
+ pvname(proc->proc_name, vp->vers_num);
+ if (mtflag)
+ f_print(fout, "_arg, &result_%d, clnt);\n",
+ i);
+ else
+ f_print(fout, "_arg, clnt);\n");
+
+ } else if (streq(proc->args.decls->decl.type, "void")) {
+ if (mtflag)
+ f_print(fout, "(&result_%d, clnt);\n", i);
+ else
+ f_print(fout, "(clnt);\n");
+ }
+ else {
+ f_print(fout, "(");
+ for (l = proc->args.decls; l != NULL; l = l->next) {
+ pvname(proc->proc_name, vp->vers_num);
+ f_print(fout, "_%s, ", l->decl.name);
+ }
+ if (mtflag)
+ f_print(fout, "&result_%d, ", i);
+
+ f_print(fout, "clnt);\n");
+ }
+ if (mtflag) {
+ f_print(fout, "\tif (retval_%d != RPC_SUCCESS) {\n", i);
+
+ } else {
+ f_print(fout, "\tif (result_%d == (", i);
+ ptype(proc->res_prefix, proc->res_type, 1);
+ f_print(fout, "*) NULL) {\n");
+ }
+ f_print(fout, "\t\tclnt_perror(clnt, \"call failed\");\n");
+ f_print(fout, "\t}\n");
+ }
+
+ f_print(fout, "#ifndef\tDEBUG\n");
+ f_print(fout, "\tclnt_destroy(clnt);\n");
+ f_print(fout, "#endif\t /* DEBUG */\n");
+ f_print(fout, "}\n");
+}
+
+static void
+write_sample_server(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");
+ if (!mtflag) {
+ return_type(proc);
+ f_print(fout, "*\n");
+ } else
+ f_print(fout, "bool_t\n");
+ if (Cflag || mtflag)
+ pvname_svc(proc->proc_name, vp->vers_num);
+ else
+ pvname(proc->proc_name, vp->vers_num);
+ printarglist(proc, "result", RQSTP, "struct svc_req *");
+
+ f_print(fout, "{\n");
+ if (!mtflag) {
+ f_print(fout, "\tstatic ");
+ if(!streq(proc->res_type, "void"))
+ return_type(proc);
+ else
+ f_print(fout, "char *");
+ /* cannot have void type */
+ f_print(fout, " result;\n", proc->res_type);
+ }
+ else
+ f_print(fout, "\tbool_t retval;\n");
+ f_print(fout,
+ "\n\t/*\n\t * insert server code here\n\t */\n\n");
+
+ if (!mtflag)
+ if(!streq(proc->res_type, "void"))
+ f_print(fout, "\treturn (&result);\n}\n");
+ else /* cast back to void * */
+ f_print(fout, "\treturn((void *) &result);\n}\n");
+ else
+ f_print(fout, "\treturn (retval);\n}\n");
+ }
+ /* put in sample freeing routine */
+ if (mtflag) {
+ f_print(fout, "\nint\n");
+ pvname(def->def_name, vp->vers_num);
+ if (Cflag)
+ f_print(fout,"_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)\n");
+ else {
+ f_print(fout,"_freeresult(transp, xdr_result, result)\n");
+ f_print(fout,"\tSVCXPRT *transp;\n");
+ f_print(fout,"\txdrproc_t xdr_result;\n");
+ f_print(fout,"\tcaddr_t result;\n");
+ }
+ f_print(fout, "{\n");
+ f_print(fout, "\t(void) xdr_free(xdr_result, result);\n");
+ f_print(fout,
+ "\n\t/*\n\t * Insert additional freeing code here, if needed\n\t */\n");
+ f_print(fout, "\n}\n");
+
+
+ }
+ }
+}
+
+
+
+static void
+return_type(plist)
+ proc_list *plist;
+{
+ ptype(plist->res_prefix, plist->res_type, 1);
+}
+
+void
+add_sample_msg()
+{
+ f_print(fout, "/*\n");
+ f_print(fout, " * This is sample code generated by rpcgen.\n");
+ f_print(fout, " * These are only templates and you can use them\n");
+ f_print(fout, " * as a guideline for developing your own functions.\n");
+ f_print(fout, " */\n\n");
+}
+
+void
+write_sample_clnt_main()
+{
+ list *l;
+ definition *def;
+ version_list *vp;
+
+ f_print(fout, "\n\n");
+ if(Cflag)
+ f_print(fout,"main(int argc, char *argv[])\n{\n");
+ else
+ f_print(fout, "main(argc, argv)\n\tint argc;\n\tchar *argv[];\n{\n");
+
+ f_print(fout, "\tchar *host;");
+ f_print(fout, "\n\n\tif (argc < 2) {");
+ f_print(fout, "\n\t\tprintf(\"usage: %%s server_host\\n\", argv[0]);\n");
+ f_print(fout, "\t\texit(1);\n\t}");
+ f_print(fout, "\n\thost = argv[1];\n");
+
+ for (l = defined; l != NULL; l = l->next) {
+ def = l->val;
+ if (def->def_kind != DEF_PROGRAM) {
+ continue;
+ }
+ for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+ f_print(fout, "\t");
+ pvname(def->def_name, vp->vers_num);
+ f_print(fout, "(host);\n");
+ }
+ }
+ f_print(fout, "}\n");
+}
diff --git a/usr.bin/rpcgen/rpc_scan.c b/usr.bin/rpcgen/rpc_scan.c
new file mode 100644
index 0000000..d1d03aa
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_scan.c
@@ -0,0 +1,517 @@
+/*
+ * 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
+ */
+
+#ident "@(#)rpc_scan.c 1.13 93/07/05 SMI"
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_scan.c 1.11 89/02/22 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_scan.c, Scanner for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+
+#include <sys/wait.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "rpc_scan.h"
+#include "rpc_parse.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 void unget_token __P(( token * ));
+static void findstrconst __P(( char **, char **));
+static void findchrconst __P(( char **, char **));
+static void findconst __P(( char **, char **));
+static void findkind __P(( char **, token * ));
+static int cppline __P(( char * ));
+static int directive __P(( char * ));
+static void printdirective __P(( char * ));
+static void docppline __P(( char *, int *, char ** ));
+
+/*
+ * 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 any of the 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 any of the 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;
+ int stat = 0;
+
+
+ if (pushed) {
+ pushed = 0;
+ *tokp = lasttok;
+ return;
+ }
+ commenting = 0;
+ for (;;) {
+ if (*where == 0) {
+ for (;;) {
+ if (!fgets(curline, MAXLINESIZE, fin)) {
+ tokp->kind = TOK_EOF;
+ /* now check if cpp returned non NULL value */
+ waitpid(childpid, &stat, WUNTRACED);
+ if (stat > 0) {
+ /* Set return value from rpcgen */
+ nonfatalerrors = stat >> 8;
+ }
+ *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) {
+ for (where++; *where; where++) {
+ if (endcomment(where)) {
+ where++;
+ commenting--;
+ break;
+ }
+ }
+ } 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 '\'':
+ tokp->kind = TOK_CHARCONST;
+ findchrconst(&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 void
+unget_token(tokp)
+ token *tokp;
+{
+ lasttok = *tokp;
+ pushed = 1;
+}
+
+static void
+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 void
+findchrconst(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;
+ if (size != 3) {
+ error("empty char string");
+ }
+ *val = alloc(size + 1);
+ (void) strncpy(*val, *str, size);
+ (*val)[size] = 0;
+ *str = p;
+}
+
+static void
+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_HYPER, "hyper"},
+ {TOK_FLOAT, "float"},
+ {TOK_DOUBLE, "double"},
+ {TOK_QUAD, "quadruple"},
+ {TOK_STRING, "string"},
+ {TOK_PROGRAM, "program"},
+ {TOK_VERSION, "version"},
+ {TOK_EOF, "??????"},
+};
+
+static void
+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 int
+cppline(line)
+ char *line;
+{
+ return (line == curline && *line == '#');
+}
+
+static int
+directive(line)
+ char *line;
+{
+ return (line == curline && *line == '%');
+}
+
+static void
+printdirective(line)
+ char *line;
+{
+ f_print(fout, "%s", line + 1);
+}
+
+static void
+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..cf042d0
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_scan.h
@@ -0,0 +1,133 @@
+/*
+ * 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
+ */
+#pragma ident "@(#)rpc_scan.h 1.11 94/05/15 SMI"
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
+/* The copyright notice above does not evidence any */
+/* actual or intended publication of such source code. */
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+* PROPRIETARY NOTICE (Combined)
+*
+* This source code is unpublished proprietary information
+* constituting, or derived under license from AT&T's UNIX(r) System V.
+* In addition, portions of such source code were derived from Berkeley
+* 4.3 BSD under license from the Regents of the University of
+* California.
+*
+*
+*
+* Copyright Notice
+*
+* Notice of copyright on this source code product does not indicate
+* publication.
+*
+* (c) 1986,1987,1988.1989 Sun Microsystems, Inc
+* (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
+* All rights reserved.
+*/
+
+/* @(#)rpc_scan.h 1.3 90/08/29 (C) 1987 SMI */
+
+/*
+ * rpc_scan.h, Definitions for the RPCL scanner
+ */
+
+/*
+ * kinds of tokens
+ */
+enum tok_kind {
+ TOK_IDENT,
+ TOK_CHARCONST,
+ 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_HYPER,
+ TOK_UNSIGNED,
+ TOK_FLOAT,
+ TOK_DOUBLE,
+ TOK_QUAD,
+ 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 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..cb26f22
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_svcout.c
@@ -0,0 +1,1086 @@
+/*
+ * 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
+ */
+
+#ident "@(#)rpc_svcout.c 1.4 90/04/13 SMI"
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_svcout.c 1.29 89/03/30 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_svcout.c, Server-skeleton outputter for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+#include <stdio.h>
+#include <string.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 char RETVAL[] = "retval";
+
+char _errbuf[256]; /* For all messages */
+
+void internal_proctype __P(( proc_list * ));
+static void write_real_program __P(( definition * ));
+static void write_program __P(( definition *, char * ));
+static void printerr __P(( char *, char * ));
+static void printif __P(( char *, char *, char *, char * ));
+static void write_inetmost __P(( char * ));
+static void print_return __P(( char * ));
+static void print_pmapunset __P(( char * ));
+static void print_err_message __P(( char * ));
+static void write_timeout_func __P(( void ));
+static void write_pm_most __P(( char *, int ));
+static void write_rpc_svc_fg __P(( char *, char * ));
+static void open_log_file __P(( char *, char * ));
+static void write_msg_out __P(( void ));
+int nullproc __P(( proc_list * ));
+
+
+static void
+p_xdrfunc(rname, typename)
+char* rname;
+char* typename;
+{
+ if (Cflag)
+ f_print(fout, "\t\txdr_%s = (xdrproc_t) xdr_%s;\n",
+ rname, stringfix(typename));
+ else
+ f_print(fout, "\t\txdr_%s = xdr_%s;\n",
+ rname, stringfix(typename));
+}
+
+void
+internal_proctype(plist)
+ proc_list *plist;
+{
+ f_print(fout, "static ");
+ ptype(plist->res_prefix, plist->res_type, 1);
+ f_print(fout, "*");
+}
+
+
+/*
+ * write most of the service, that is, everything but the registrations.
+ */
+void
+write_most(infile, netflag, nomain)
+ char *infile; /* our name */
+ int netflag;
+ int nomain;
+{
+ if (inetdflag || pmflag) {
+ char* var_type;
+ var_type = (nomain? "extern" : "static");
+ f_print(fout, "%s int _rpcpmstart;", var_type);
+ f_print(fout, "\t\t/* Started by a port monitor ? */\n");
+ if (!tirpcflag) {
+ f_print(fout, "%s int _rpcfdtype;", var_type);
+ f_print(fout, "\n\t\t /* Whether Stream or \
+Datagram ? */\n");
+ }
+
+ if (timerflag) {
+ f_print(fout, " /* States a server can be in \
+wrt request */\n\n");
+ f_print(fout, "#define\t_IDLE 0\n");
+ f_print(fout, "#define\t_SERVED 1\n");
+ f_print(fout, "#define\t_SERVING 2\n\n");
+ f_print(fout, "static int _rpcsvcstate = _IDLE;");
+ f_print(fout, "\t /* Set when a request is \
+serviced */\n");
+
+ if (mtflag) {
+ f_print(fout, "mutex_t _svcstate_lock;");
+ f_print(fout, "\t\t\t/* Mutex lock for variable _rpcsvcstate */\n");
+
+ }
+
+ }
+
+ write_svc_aux(nomain);
+ }
+ /* write out dispatcher and stubs */
+ write_programs(nomain? (char *)NULL : "static");
+
+ if (nomain)
+ return;
+
+ f_print(fout, "\nmain()\n");
+ f_print(fout, "{\n");
+ if (inetdflag) {
+ write_inetmost(infile);
+ /* Includes call to write_rpc_svc_fg() */
+ } else {
+ if (tirpcflag) {
+ if (netflag) {
+ f_print(fout,
+ "\tregister SVCXPRT *%s;\n", TRANSP);
+ f_print(fout,
+ "\tstruct netconfig *nconf = NULL;\n");
+ }
+ f_print(fout, "\tpid_t pid;\n");
+ f_print(fout, "\tint i;\n");
+ f_print(fout, "\tchar mname[FMNAMESZ + 1];\n\n");
+
+ if (mtflag & timerflag)
+ f_print(fout, "\tmutex_init(&_svcstate_lock, USYNC_THREAD, NULL);\n");
+
+ write_pm_most(infile, netflag);
+ f_print(fout, "\telse {\n");
+ write_rpc_svc_fg(infile, "\t\t");
+ f_print(fout, "\t}\n");
+ } else {
+ f_print(fout, "\tregister SVCXPRT *%s;\n", TRANSP);
+ f_print(fout, "\n");
+ print_pmapunset("\t");
+ }
+ }
+
+ if (logflag && !inetdflag) {
+ open_log_file(infile, "\t");
+ }
+}
+
+/*
+ * write a registration for the given transport
+ */
+void
+write_netid_register(transp)
+ char *transp;
+{
+ list *l;
+ definition *def;
+ version_list *vp;
+ char *sp;
+ char tmpbuf[32];
+
+ sp = "";
+ f_print(fout, "\n");
+ f_print(fout, "%s\tnconf = getnetconfigent(\"%s\");\n", sp, transp);
+ f_print(fout, "%s\tif (nconf == NULL) {\n", sp);
+ (void) sprintf(_errbuf, "cannot find %s netid.", transp);
+ sprintf(tmpbuf, "%s\t\t", sp);
+ print_err_message(tmpbuf);
+ f_print(fout, "%s\t\texit(1);\n", sp);
+ f_print(fout, "%s\t}\n", sp);
+ f_print(fout, "%s\t%s = svc_tli_create(RPC_ANYFD, nconf, 0, 0, 0);\n",
+ sp, TRANSP, transp);
+ f_print(fout, "%s\tif (%s == NULL) {\n", sp, TRANSP);
+ (void) sprintf(_errbuf, "cannot create %s service.", transp);
+ print_err_message(tmpbuf);
+ f_print(fout, "%s\t\texit(1);\n", sp);
+ f_print(fout, "%s\t}\n", sp);
+
+ 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,
+ "%s\t(void) rpcb_unset(%s, %s, nconf);\n",
+ sp, def->def_name, vp->vers_name);
+ f_print(fout,
+ "%s\tif (!svc_reg(%s, %s, %s, ",
+ sp, TRANSP, def->def_name, vp->vers_name);
+ pvname(def->def_name, vp->vers_num);
+ f_print(fout, ", nconf)) {\n");
+ (void) sprintf(_errbuf,
+ "unable to register (%s, %s, %s).",
+ def->def_name, vp->vers_name, transp);
+ print_err_message(tmpbuf);
+ f_print(fout, "%s\t\texit(1);\n", sp);
+ f_print(fout, "%s\t}\n", sp);
+ }
+ }
+ f_print(fout, "%s\tfreenetconfigent(nconf);\n", sp);
+}
+
+/*
+ * write a registration for the given transport for TLI
+ */
+void
+write_nettype_register(transp)
+ char *transp;
+{
+ 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) {
+ continue;
+ }
+ for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+ f_print(fout, "\tif (!svc_create(");
+ pvname(def->def_name, vp->vers_num);
+ f_print(fout, ", %s, %s, \"%s\")) {\n",
+ def->def_name, vp->vers_name, transp);
+ (void) sprintf(_errbuf,
+ "unable to create (%s, %s) for %s.",
+ def->def_name, vp->vers_name, transp);
+ print_err_message("\t\t");
+ f_print(fout, "\t\texit(1);\n");
+ f_print(fout, "\t}\n");
+ }
+ }
+}
+
+/*
+ * write the rest of the service
+ */
+void
+write_rest()
+{
+ f_print(fout, "\n");
+ if (inetdflag) {
+ f_print(fout, "\tif (%s == (SVCXPRT *)NULL) {\n", TRANSP);
+ (void) sprintf(_errbuf, "could not create a handle");
+ print_err_message("\t\t");
+ f_print(fout, "\t\texit(1);\n");
+ f_print(fout, "\t}\n");
+ if (timerflag) {
+ f_print(fout, "\tif (_rpcpmstart) {\n");
+ f_print(fout,
+ "\t\t(void) signal(SIGALRM, %s closedown);\n",
+ Cflag? "(SIG_PF)":"(void(*)())");
+ f_print(fout, "\t\t(void) \
+alarm(_RPCSVC_CLOSEDOWN/2);\n");
+ f_print(fout, "\t}\n");
+ }
+ }
+ f_print(fout, "\tsvc_run();\n");
+ (void) sprintf(_errbuf, "svc_run returned");
+ print_err_message("\t");
+ f_print(fout, "\texit(1);\n");
+ f_print(fout, "\t/* NOTREACHED */\n");
+ f_print(fout, "}\n");
+}
+
+void
+write_programs(storage)
+ char *storage;
+{
+ list *l;
+ definition *def;
+
+ /* write out stubs for procedure definitions */
+ for (l = defined; l != NULL; l = l->next) {
+ def = (definition *) l->val;
+ if (def->def_kind == DEF_PROGRAM) {
+ write_real_program(def);
+ }
+ }
+
+ /* write out dispatcher for each program */
+ for (l = defined; l != NULL; l = l->next) {
+ def = (definition *) l->val;
+ if (def->def_kind == DEF_PROGRAM) {
+ write_program(def, storage);
+ }
+ }
+
+
+}
+
+/*
+ * write out definition of internal function (e.g. _printmsg_1(...))
+ * which calls server's defintion of actual function (e.g. printmsg_1(...)).
+ * Unpacks single user argument of printmsg_1 to call-by-value format
+ * expected by printmsg_1.
+ */
+static void
+write_real_program(def)
+ definition *def;
+{
+ version_list *vp;
+ proc_list *proc;
+ decl_list *l;
+
+ if (!newstyle) return; /* not needed for old style */
+ for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+ for (proc = vp->procs; proc != NULL; proc = proc->next) {
+ f_print(fout, "\n");
+ if (!mtflag)
+ internal_proctype(proc);
+ else
+ f_print(fout, "int");
+ f_print(fout, "\n_");
+ pvname(proc->proc_name, vp->vers_num);
+ if (Cflag) {
+ f_print(fout, "(");
+ /* arg name */
+ if (proc->arg_num > 1)
+ f_print(fout, proc->args.argname);
+ else
+ ptype(proc->args.decls->decl.prefix,
+ proc->args.decls->decl.type, 0);
+ if (mtflag) {
+ f_print(fout, " *argp, void *%s, struct svc_req *%s)\n",
+ RESULT, RQSTP);
+
+
+ }
+ else
+ f_print(fout, " *argp, struct svc_req *%s)\n",
+ RQSTP);
+
+ } else {
+ if (mtflag)
+ f_print(fout, "(argp, %s, %s)\n", RESULT, RQSTP);
+ else
+ f_print(fout, "(argp, %s)\n", RQSTP);
+ /* arg name */
+ if (proc->arg_num > 1)
+ f_print(fout, "\t%s *argp;\n",
+ proc->args.argname);
+ else {
+ f_print(fout, "\t");
+ ptype(proc->args.decls->decl.prefix,
+ proc->args.decls->decl.type, 0);
+ f_print(fout, " *argp;\n");
+ }
+ if (mtflag)
+ f_print(fout, "\tvoid *%s;\n", RESULT);
+ f_print(fout, "\tstruct svc_req *%s;\n", RQSTP);
+ }
+
+ f_print(fout, "{\n");
+ f_print(fout, "\treturn (");
+ if (Cflag || mtflag) /* for mtflag, arguments are different */
+ pvname_svc(proc->proc_name, vp->vers_num);
+ else
+ pvname(proc->proc_name, vp->vers_num);
+ f_print(fout, "(");
+ if (proc->arg_num < 2) { /* single argument */
+ if (!streq(proc->args.decls->decl.type, "void"))
+ f_print(fout, "*argp, "); /* non-void */
+ } else {
+ for (l = proc->args.decls; l != NULL;
+ l = l->next)
+ f_print(fout, "argp->%s, ",
+ l->decl.name);
+ }
+ if (mtflag)
+ f_print(fout, "%s, ",RESULT);
+ f_print(fout, "%s));\n}\n", RQSTP);
+ }
+ }
+}
+
+static void
+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);
+
+ if (Cflag) {
+ f_print(fout, "(struct svc_req *%s, ", RQSTP);
+ f_print(fout, "register SVCXPRT *%s)\n", TRANSP);
+ } else {
+ f_print(fout, "(%s, %s)\n", RQSTP, TRANSP);
+ f_print(fout, " struct svc_req *%s;\n", RQSTP);
+ f_print(fout, " register 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 (proc->arg_num < 2) { /* single argument */
+ if (streq(proc->args.decls->decl.type,
+ "void")) {
+ continue;
+ }
+ filled = 1;
+ f_print(fout, "\t\t");
+ ptype(proc->args.decls->decl.prefix,
+ proc->args.decls->decl.type, 0);
+ pvname(proc->proc_name, vp->vers_num);
+ f_print(fout, "_arg;\n");
+
+ } else {
+ filled = 1;
+ f_print(fout, "\t\t%s", proc->args.argname);
+ f_print(fout, " ");
+ 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);
+
+ if (mtflag) {
+ f_print(fout, "\tunion {\n");
+ for (proc = vp->procs; proc != NULL; proc = proc->next) {
+ f_print(fout, "\t\t");
+ ptype(proc->res_prefix, proc->res_type, 0);
+ pvname(proc->proc_name, vp->vers_num);
+ f_print(fout, "_res;\n");
+ }
+ f_print(fout, "\t} %s;\n", RESULT);
+ f_print(fout, "\tbool_t %s;\n", RETVAL);
+
+ } else
+ f_print(fout, "\tchar *%s;\n", RESULT);
+
+ if (Cflag) {
+ f_print(fout, "\txdrproc_t xdr_%s, xdr_%s;\n",
+ ARG, RESULT);
+ if (mtflag)
+ f_print(fout,
+ "\tbool_t (*%s)(char *, void *, struct svc_req *);\n",
+ ROUTINE);
+ else
+ f_print(fout,
+ "\tchar *(*%s)(char *, struct svc_req *);\n",
+ ROUTINE);
+ } else {
+ f_print(fout,
+ "\tbool_t (*xdr_%s)(), (*xdr_%s)();\n",
+ ARG, RESULT);
+ if (mtflag)
+ f_print(fout, "\tbool_t (*%s)();\n", ROUTINE);
+ else
+ f_print(fout, "\tchar *(*%s)();\n", ROUTINE);
+ }
+ f_print(fout, "\n");
+
+ if (timerflag) {
+ if (mtflag)
+ f_print(fout, "\tmutex_lock(&_svcstate_lock);\n");
+
+ f_print(fout, "\t_rpcsvcstate = _SERVING;\n");
+ if (mtflag)
+ f_print(fout, "\tmutex_unlock(&_svcstate_lock);\n");
+ }
+
+ f_print(fout, "\tswitch (%s->rq_proc) {\n", RQSTP);
+ if (!nullproc(vp->procs)) {
+ f_print(fout, "\tcase NULLPROC:\n");
+ f_print(fout,
+ Cflag
+ ? "\t\t(void) svc_sendreply(%s,\n\t\t\t\
+(xdrproc_t) xdr_void, (char *)NULL);\n"
+ : "\t\t(void) svc_sendreply(%s, xdr_void,\n\t\t\t\
+(char *)NULL);\n",
+ TRANSP);
+ print_return("\t\t");
+ f_print(fout, "\n");
+ }
+ for (proc = vp->procs; proc != NULL; proc = proc->next) {
+ f_print(fout, "\tcase %s:\n", proc->proc_name);
+ if (proc->arg_num < 2) { /* single argument */
+ p_xdrfunc(ARG, proc->args.decls->decl.type);
+ } else {
+ p_xdrfunc(ARG, proc->args.argname);
+ }
+ p_xdrfunc(RESULT, proc->res_type);
+
+ if (Cflag)
+ if (mtflag)
+ f_print(fout,
+ "\t\t%s = (bool_t (*) (char *, void *, struct svc_req *))",
+ ROUTINE);
+ else
+ f_print(fout,
+ "\t\t%s = (char *(*)(char *, struct svc_req *)) ",
+ ROUTINE);
+ else
+ if (mtflag)
+ f_print(fout, "\t\t%s = (bool_t (*)()) ",
+ ROUTINE);
+ else
+
+ f_print(fout, "\t\t%s = (char *(*)()) ",
+ ROUTINE);
+ if (newstyle) { /* new style: calls internal routine */
+ f_print(fout, "_");
+ }
+ if ((Cflag || mtflag) && !newstyle)
+ pvname_svc(proc->proc_name, vp->vers_num);
+ else
+ 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);
+ print_return("\t\t");
+ f_print(fout, "\t}\n");
+
+ f_print(fout,
+ "\t(void) memset((char *)&%s, 0, sizeof (%s));\n",
+ ARG, ARG);
+ if (Cflag)
+ printif("getargs", TRANSP, "(caddr_t) &", ARG);
+ else
+ printif("getargs", TRANSP, "&", ARG);
+ printerr("decode", TRANSP);
+ print_return("\t\t");
+ f_print(fout, "\t}\n");
+
+ if (!mtflag)
+ if (Cflag)
+ f_print(fout, "\t%s = (*%s)((char *)&%s, %s);\n",
+ RESULT, ROUTINE, ARG, RQSTP);
+ else
+ f_print(fout, "\t%s = (*%s)(&%s, %s);\n",
+ RESULT, ROUTINE, ARG, RQSTP);
+ else
+ if (Cflag)
+ f_print(fout, "\t%s = (bool_t) (*%s)((char *)&%s, (void *)&%s, %s);\n",
+ RETVAL, ROUTINE, ARG, RESULT, RQSTP);
+ else
+ f_print(fout, "\t%s = (bool_t) (*%s)(&%s, &%s, %s);\n",
+ RETVAL, ROUTINE, ARG, RESULT, RQSTP);
+
+
+
+
+ if (mtflag)
+ f_print(fout,
+ "\tif (%s > 0 && !svc_sendreply(%s, xdr_%s, (char *)&%s)) {\n",
+ RETVAL, TRANSP, RESULT, RESULT);
+ else
+ 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");
+
+ if (Cflag)
+ printif("freeargs", TRANSP, "(caddr_t) &", ARG);
+ else
+ printif("freeargs", TRANSP, "&", ARG);
+ (void) sprintf(_errbuf, "unable to free arguments");
+ print_err_message("\t\t");
+ f_print(fout, "\t\texit(1);\n");
+ f_print(fout, "\t}\n");
+ /* print out free routine */
+ if (mtflag) {
+ f_print(fout,"\tif (!");
+ pvname(def->def_name, vp->vers_num);
+ f_print(fout,"_freeresult(%s, xdr_%s, (caddr_t) &%s))\n",
+ TRANSP, RESULT, RESULT);
+ (void) sprintf(_errbuf, "unable to free results");
+ print_err_message("\t\t");
+ f_print(fout, "\n");
+ };
+ print_return("\t");
+ f_print(fout, "}\n");
+ }
+}
+
+static void
+printerr(err, transp)
+ char *err;
+ char *transp;
+{
+ f_print(fout, "\t\tsvcerr_%s(%s);\n", err, transp);
+}
+
+static void
+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);
+}
+
+int
+nullproc(proc)
+ proc_list *proc;
+{
+ for (; proc != NULL; proc = proc->next) {
+ if (streq(proc->proc_num, "0")) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static void
+write_inetmost(infile)
+ char *infile;
+{
+ f_print(fout, "\tregister SVCXPRT *%s;\n", TRANSP);
+ f_print(fout, "\tint sock;\n");
+ f_print(fout, "\tint proto;\n");
+ f_print(fout, "\tstruct sockaddr_in saddr;\n");
+ f_print(fout, "\tint asize = sizeof (saddr);\n");
+ f_print(fout, "\n");
+ f_print(fout,
+ "\tif (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {\n");
+ f_print(fout, "\t\tint ssize = sizeof (int);\n\n");
+ f_print(fout, "\t\tif (saddr.sin_family != AF_INET)\n");
+ f_print(fout, "\t\t\texit(1);\n");
+ f_print(fout, "\t\tif (getsockopt(0, SOL_SOCKET, SO_TYPE,\n");
+ f_print(fout, "\t\t\t\t(char *)&_rpcfdtype, &ssize) == -1)\n");
+ f_print(fout, "\t\t\texit(1);\n");
+ f_print(fout, "\t\tsock = 0;\n");
+ f_print(fout, "\t\t_rpcpmstart = 1;\n");
+ f_print(fout, "\t\tproto = 0;\n");
+ open_log_file(infile, "\t\t");
+ f_print(fout, "\t} else {\n");
+ write_rpc_svc_fg(infile, "\t\t");
+ f_print(fout, "\t\tsock = RPC_ANYSOCK;\n");
+ print_pmapunset("\t\t");
+ f_print(fout, "\t}\n");
+}
+
+static void
+print_return(space)
+ char *space;
+{
+ if (exitnow)
+ f_print(fout, "%sexit(0);\n", space);
+ else {
+ if (timerflag) {
+ if (mtflag)
+ f_print(fout, "%smutex_lock(&_svcstate_lock);\n", space);
+ f_print(fout, "%s_rpcsvcstate = _SERVED;\n", space);
+ if (mtflag)
+ f_print(fout, "%smutex_unlock(&_svcstate_lock);\n", space);
+ }
+ f_print(fout, "%sreturn;\n", space);
+ }
+}
+
+static void
+print_pmapunset(space)
+ char *space;
+{
+ 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, "%s(void) pmap_unset(%s, %s);\n",
+ space, def->def_name, vp->vers_name);
+ }
+ }
+ }
+}
+
+static void
+print_err_message(space)
+ char *space;
+{
+ if (logflag)
+ f_print(fout, "%ssyslog(LOG_ERR, \"%s\");\n", space, _errbuf);
+ else if (inetdflag || pmflag)
+ f_print(fout, "%s_msgout(\"%s\");\n", space, _errbuf);
+ else
+ f_print(fout, "%sfprintf(stderr, \"%s\");\n", space, _errbuf);
+}
+
+/*
+ * Write the server auxiliary function (_msgout, timeout)
+ */
+void
+write_svc_aux(nomain)
+ int nomain;
+{
+ if (!logflag)
+ write_msg_out();
+ if (!nomain)
+ write_timeout_func();
+}
+
+/*
+ * Write the _msgout function
+ */
+
+static void
+write_msg_out(void)
+{
+ f_print(fout, "\n");
+/*
+ * Avoid making _msgout() static -- it's useful to have it visible
+ * in the toplevel RPC server code.
+ */
+ f_print(fout, "static\n");
+
+ if (!Cflag) {
+ f_print(fout, "void _msgout(msg)\n");
+ f_print(fout, "\tchar *msg;\n");
+ } else {
+ f_print(fout, "void _msgout(char* msg)\n");
+ }
+ f_print(fout, "{\n");
+ f_print(fout, "#ifdef RPC_SVC_FG\n");
+ if (inetdflag || pmflag)
+ f_print(fout, "\tif (_rpcpmstart)\n");
+ f_print(fout, "\t\tsyslog(LOG_ERR, msg);\n");
+ f_print(fout, "\telse\n");
+ f_print(fout,
+ "\t\t(void) fprintf(stderr, \"%%s\\n\", msg);\n");
+ f_print(fout, "#else\n");
+ f_print(fout, "\tsyslog(LOG_ERR, msg);\n");
+ f_print(fout, "#endif\n");
+ f_print(fout, "}\n");
+}
+
+/*
+ * Write the timeout function
+ */
+static void
+write_timeout_func(void)
+{
+ if (!timerflag)
+ return;
+
+ f_print(fout, "\n");
+ f_print(fout, "static void\n");
+ if (!Cflag) {
+ f_print(fout, "closedown(sig)\n");
+ f_print(fout, "\tint sig;\n");
+ } else
+ f_print(fout, "closedown(int sig)\n");
+ f_print(fout, "{\n");
+ if (mtflag)
+ f_print(fout, "\tmutex_lock(&_svcstate_lock);\n");
+ f_print(fout, "\tif (_rpcsvcstate == _IDLE) {\n");
+ f_print(fout, "\t\textern fd_set svc_fdset;\n");
+ f_print(fout, "\t\tstatic int size;\n");
+ f_print(fout, "\t\tint i, openfd;\n");
+ if (tirpcflag && pmflag) {
+ f_print(fout, "\t\tstruct t_info tinfo;\n\n");
+ f_print(fout,
+ "\t\tif (!t_getinfo(0, &tinfo) && (tinfo.servtype == T_CLTS))\n");
+ } else {
+ f_print(fout, "\n\t\tif (_rpcfdtype == SOCK_DGRAM)\n");
+ }
+ f_print(fout, "\t\t\texit(0);\n");
+ f_print(fout, "\t\tif (size == 0) {\n");
+ if (tirpcflag) {
+ f_print(fout, "\t\t\tstruct rlimit rl;\n\n");
+ f_print(fout, "\t\t\trl.rlim_max = 0;\n");
+ f_print(fout, "\t\t\tgetrlimit(RLIMIT_NOFILE, &rl);\n");
+ f_print(fout, "\t\t\tif ((size = rl.rlim_max) == 0) {\n");
+
+ if (mtflag)
+ f_print(fout, "\t\t\t\tmutex_unlock(&_svcstate_lock);\n");
+
+ f_print(fout, "\t\t\t\treturn;\n\t\t\t}\n");
+ } else {
+ f_print(fout, "\t\t\tsize = getdtablesize();\n");
+ }
+ f_print(fout, "\t\t}\n");
+ f_print(fout,
+ "\t\tfor (i = 0, openfd = 0; i < size && openfd < 2; i++)\n");
+ f_print(fout, "\t\t\tif (FD_ISSET(i, &svc_fdset))\n");
+ f_print(fout, "\t\t\t\topenfd++;\n");
+ f_print(fout, "\t\tif (openfd <= 1)\n");
+ f_print(fout, "\t\t\texit(0);\n");
+ f_print(fout, "\t}\n");
+ f_print(fout, "\tif (_rpcsvcstate == _SERVED)\n");
+ f_print(fout, "\t\t_rpcsvcstate = _IDLE;\n\n");
+ if (mtflag)
+ f_print(fout, "\tmutex_unlock(&_svcstate_lock);\n");
+
+ f_print(fout, "\t(void) signal(SIGALRM, %s closedown);\n",
+ Cflag? "(SIG_PF)" : "(void(*)())");
+ f_print(fout, "\t(void) alarm(_RPCSVC_CLOSEDOWN/2);\n");
+ f_print(fout, "}\n");
+
+}
+
+/*
+ * Write the most of port monitor support
+ */
+static void
+write_pm_most(infile, netflag)
+ char *infile;
+ int netflag;
+{
+ list *l;
+ definition *def;
+ version_list *vp;
+
+ f_print(fout, "\tif (!ioctl(0, I_LOOK, mname) &&\n");
+ f_print(fout, "\t\t(!strcmp(mname, \"sockmod\") ||");
+ f_print(fout, " !strcmp(mname, \"timod\"))) {\n");
+ f_print(fout, "\t\tchar *netid;\n");
+ if (!netflag) { /* Not included by -n option */
+ f_print(fout, "\t\tstruct netconfig *nconf = NULL;\n");
+ f_print(fout, "\t\tSVCXPRT *%s;\n", TRANSP);
+ }
+ if (timerflag)
+ f_print(fout, "\t\tint pmclose;\n");
+/*
+ * Not necessary, defined in /usr/include/stdlib
+ * f_print(fout, "\t\textern char *getenv();\n");
+ */
+ f_print(fout, "\n");
+ f_print(fout, "\t\t_rpcpmstart = 1;\n");
+ open_log_file(infile, "\t\t");
+ f_print(fout, "\n\t\tif ((netid = \
+getenv(\"NLSPROVIDER\")) == NULL) {\n");
+
+ if (timerflag) {
+ f_print(fout, "\t\t/* started from inetd */\n");
+ f_print(fout, "\t\t\tpmclose = 1;\n");
+ }
+ f_print(fout,
+ "\t\t} else {\n");
+ f_print(fout, "\t\t\tif ((nconf = getnetconfigent(netid)) == NULL)\n");
+ sprintf(_errbuf, "cannot get transport info");
+ print_err_message("\t\t\t\t");
+ if (timerflag)
+ f_print(fout, "\n\t\t\tpmclose = \
+(t_getstate(0) != T_DATAXFER);\n");
+ f_print(fout, "\t\t}\n");
+ /*
+ * A kludgy support for inetd services. Inetd only works with
+ * sockmod, and RPC works only with timod, hence all this jugglery
+ */
+ f_print(fout, "\t\tif (strcmp(mname, \"sockmod\") == 0) {\n");
+ f_print(fout,
+ "\t\t\tif (ioctl(0, I_POP, 0) || \
+ioctl(0, I_PUSH, \"timod\")) {\n");
+ sprintf(_errbuf, "could not get the right module");
+ print_err_message("\t\t\t\t");
+ f_print(fout, "\t\t\t\texit(1);\n");
+ f_print(fout, "\t\t\t}\n");
+ f_print(fout, "\t\t}\n");
+ f_print(fout,
+ "\t\tif ((%s = svc_tli_create(0, nconf, NULL, 0, 0)) \
+== NULL) {\n",
+ TRANSP);
+ sprintf(_errbuf, "cannot create server handle");
+ print_err_message("\t\t\t");
+ f_print(fout, "\t\t\texit(1);\n");
+ f_print(fout, "\t\t}\n");
+ f_print(fout, "\t\tif (nconf)\n");
+ f_print(fout, "\t\t\tfreenetconfigent(nconf);\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\tif (!svc_reg(%s, %s, %s, ",
+ TRANSP, def->def_name, vp->vers_name);
+ pvname(def->def_name, vp->vers_num);
+ f_print(fout, ", 0)) {\n");
+ (void) sprintf(_errbuf, "unable to register (%s, %s).",
+ def->def_name, vp->vers_name);
+ print_err_message("\t\t\t");
+ f_print(fout, "\t\t\texit(1);\n");
+ f_print(fout, "\t\t}\n");
+ }
+ }
+ if (timerflag) {
+ f_print(fout, "\t\tif (pmclose) {\n");
+ f_print(fout, "\t\t\t(void) signal(SIGALRM, %s closedown);\n",
+ Cflag? "(SIG_PF)" : "(void(*)())");
+ f_print(fout, "\t\t\t(void) alarm(_RPCSVC_CLOSEDOWN/2);\n");
+ f_print(fout, "\t\t}\n");
+ }
+ f_print(fout, "\t\tsvc_run();\n");
+ f_print(fout, "\t\texit(1);\n");
+ f_print(fout, "\t\t/* NOTREACHED */\n");
+ f_print(fout, "\t}");
+}
+
+/*
+ * Support for backgrounding the server if self started.
+ */
+static void
+write_rpc_svc_fg(infile, sp)
+ char *infile;
+ char *sp;
+{
+ f_print(fout, "#ifndef RPC_SVC_FG\n");
+ f_print(fout, "%sint size;\n", sp);
+ if (tirpcflag)
+ f_print(fout, "%sstruct rlimit rl;\n", sp);
+ if (inetdflag)
+ f_print(fout, "%sint pid, i;\n\n", sp);
+ f_print(fout, "%spid = fork();\n", sp);
+ f_print(fout, "%sif (pid < 0) {\n", sp);
+ f_print(fout, "%s\tperror(\"cannot fork\");\n", sp);
+ f_print(fout, "%s\texit(1);\n", sp);
+ f_print(fout, "%s}\n", sp);
+ f_print(fout, "%sif (pid)\n", sp);
+ f_print(fout, "%s\texit(0);\n", sp);
+ /* get number of file descriptors */
+ if (tirpcflag) {
+ f_print(fout, "%srl.rlim_max = 0;\n", sp);
+ f_print(fout, "%sgetrlimit(RLIMIT_NOFILE, &rl);\n", sp);
+ f_print(fout, "%sif ((size = rl.rlim_max) == 0)\n", sp);
+ f_print(fout, "%s\texit(1);\n", sp);
+ } else {
+ f_print(fout, "%ssize = getdtablesize();\n", sp);
+ }
+
+ f_print(fout, "%sfor (i = 0; i < size; i++)\n", sp);
+ f_print(fout, "%s\t(void) close(i);\n", sp);
+ /* Redirect stderr and stdout to console */
+ f_print(fout, "%si = open(\"/dev/console\", 2);\n", sp);
+ f_print(fout, "%s(void) dup2(i, 1);\n", sp);
+ f_print(fout, "%s(void) dup2(i, 2);\n", sp);
+ /* This removes control of the controlling terminal */
+ if (tirpcflag)
+ f_print(fout, "%ssetsid();\n", sp);
+ else {
+ f_print(fout, "%si = open(\"/dev/tty\", 2);\n", sp);
+ f_print(fout, "%sif (i >= 0) {\n", sp);
+ f_print(fout,
+ "%s\t(void) ioctl(i, TIOCNOTTY, (char *)NULL);\n", sp);
+ f_print(fout, "%s\t(void) close(i);\n", sp);
+ f_print(fout, "%s}\n", sp);
+ }
+ if (!logflag)
+ open_log_file(infile, sp);
+ f_print(fout, "#endif\n");
+ if (logflag)
+ open_log_file(infile, sp);
+}
+
+static void
+open_log_file(infile, sp)
+ char *infile;
+ char *sp;
+{
+ char *s;
+
+ s = strrchr(infile, '.');
+ if (s)
+ *s = '\0';
+ f_print(fout, "%sopenlog(\"%s\", LOG_PID, LOG_DAEMON);\n", sp, infile);
+ if (s)
+ *s = '.';
+}
+
+
+
+
+/*
+ * write a registration for the given transport for Inetd
+ */
+void
+write_inetd_register(transp)
+ char *transp;
+{
+ list *l;
+ definition *def;
+ version_list *vp;
+ char *sp;
+ int isudp;
+ char tmpbuf[32];
+
+ if (inetdflag)
+ sp = "\t";
+ else
+ sp = "";
+ if (streq(transp, "udp"))
+ isudp = 1;
+ else
+ isudp = 0;
+ f_print(fout, "\n");
+ if (inetdflag) {
+ f_print(fout,
+ "\tif ((_rpcfdtype == 0) || (_rpcfdtype == %s)) {\n",
+ isudp ? "SOCK_DGRAM" : "SOCK_STREAM");
+ }
+ f_print(fout, "%s\t%s = svc%s_create(%s",
+ sp, TRANSP, transp, inetdflag? "sock": "RPC_ANYSOCK");
+ if (!isudp)
+ f_print(fout, ", 0, 0");
+ f_print(fout, ");\n");
+ f_print(fout, "%s\tif (%s == NULL) {\n", sp, TRANSP);
+ (void) sprintf(_errbuf, "cannot create %s service.", transp);
+ (void) sprintf(tmpbuf, "%s\t\t", sp);
+ print_err_message(tmpbuf);
+ f_print(fout, "%s\t\texit(1);\n", sp);
+ f_print(fout, "%s\t}\n", sp);
+
+ if (inetdflag) {
+ f_print(fout, "%s\tif (!_rpcpmstart)\n\t", sp);
+ f_print(fout, "%s\tproto = IPPROTO_%s;\n",
+ sp, isudp ? "UDP": "TCP");
+ }
+ 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, "%s\tif (!svc_register(%s, %s, %s, ",
+ sp, TRANSP, def->def_name, vp->vers_name);
+ pvname(def->def_name, vp->vers_num);
+ if (inetdflag)
+ f_print(fout, ", proto)) {\n");
+ else
+ f_print(fout, ", IPPROTO_%s)) {\n",
+ isudp ? "UDP": "TCP");
+ (void) sprintf(_errbuf,
+ "unable to register (%s, %s, %s).",
+ def->def_name, vp->vers_name, transp);
+ print_err_message(tmpbuf);
+ f_print(fout, "%s\t\texit(1);\n", sp);
+ f_print(fout, "%s\t}\n", sp);
+ }
+ }
+ if (inetdflag)
+ f_print(fout, "\t}\n");
+}
diff --git a/usr.bin/rpcgen/rpc_tblout.c b/usr.bin/rpcgen/rpc_tblout.c
new file mode 100644
index 0000000..f8b56798
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_tblout.c
@@ -0,0 +1,172 @@
+/*
+ * 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
+ */
+
+#ident "@(#)rpc_tblout.c 1.11 93/07/05 SMI"
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_tblout.c 1.4 89/02/22 (C) 1988 SMI";
+#endif
+
+/*
+ * rpc_tblout.c, Dispatch table outputter for the RPC protocol compiler
+ * Copyright (C) 1989, Sun Microsystems, Inc.
+ */
+#include <stdio.h>
+#include <string.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+#define TABSIZE 8
+#define TABCOUNT 5
+#define TABSTOP (TABSIZE*TABCOUNT)
+
+static char tabstr[TABCOUNT+1] = "\t\t\t\t\t";
+
+static char tbl_hdr[] = "struct rpcgen_table %s_table[] = {\n";
+static char tbl_end[] = "};\n";
+
+static char null_entry[] = "\n\t(char *(*)())0,\n\
+ \t(xdrproc_t) xdr_void,\t\t\t0,\n\
+ \t(xdrproc_t) xdr_void,\t\t\t0,\n";
+
+
+static char tbl_nproc[] = "int %s_nproc =\n\tsizeof(%s_table)/sizeof(%s_table[0]);\n\n";
+
+extern int nullproc __P(( proc_list * ));
+static void write_table __P(( definition * ));
+static void printit __P(( char *, char * ));
+
+void
+write_tables()
+{
+ list *l;
+ definition *def;
+
+ f_print(fout, "\n");
+ for (l = defined; l != NULL; l = l->next) {
+ def = (definition *) l->val;
+ if (def->def_kind == DEF_PROGRAM) {
+ write_table(def);
+ }
+ }
+}
+
+static void
+write_table(def)
+ definition *def;
+{
+ version_list *vp;
+ proc_list *proc;
+ int current;
+ int expected;
+ char progvers[100];
+ int warning;
+
+ for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+ warning = 0;
+ s_print(progvers, "%s_%s",
+ locase(def->def_name), vp->vers_num);
+ /* print the table header */
+ f_print(fout, tbl_hdr, progvers);
+
+ if (nullproc(vp->procs)) {
+ expected = 0;
+ } else {
+ expected = 1;
+ f_print(fout, null_entry);
+ }
+ for (proc = vp->procs; proc != NULL; proc = proc->next) {
+ current = atoi(proc->proc_num);
+ if (current != expected++) {
+ f_print(fout,
+ "\n/*\n * WARNING: table out of order\n */\n");
+ if (warning == 0) {
+ f_print(stderr,
+ "WARNING %s table is out of order\n",
+ progvers);
+ warning = 1;
+ nonfatalerrors = 1;
+ }
+ expected = current + 1;
+ }
+ f_print(fout, "\n\t(char *(*)())RPCGEN_ACTION(");
+
+ /* routine to invoke */
+ if( Cflag && !newstyle )
+ pvname_svc(proc->proc_name, vp->vers_num);
+ else {
+ if( newstyle )
+ f_print( fout, "_"); /* calls internal func */
+ pvname(proc->proc_name, vp->vers_num);
+ }
+ f_print(fout, "),\n");
+
+ /* argument info */
+ if( proc->arg_num > 1 )
+ printit((char*) NULL, proc->args.argname );
+ else
+ /* do we have to do something special for newstyle */
+ printit( proc->args.decls->decl.prefix,
+ proc->args.decls->decl.type );
+ /* result info */
+ printit(proc->res_prefix, proc->res_type);
+ }
+
+ /* print the table trailer */
+ f_print(fout, tbl_end);
+ f_print(fout, tbl_nproc, progvers, progvers, progvers);
+ }
+}
+
+static void
+printit(prefix, type)
+ char *prefix;
+ char *type;
+{
+ int len;
+ int tabs;
+
+
+ len = fprintf(fout, "\txdr_%s,", stringfix(type));
+ /* account for leading tab expansion */
+ len += TABSIZE - 1;
+ /* round up to tabs required */
+ tabs = (TABSTOP - len + TABSIZE - 1)/TABSIZE;
+ f_print(fout, "%s", &tabstr[TABCOUNT-tabs]);
+
+ if (streq(type, "void")) {
+ f_print(fout, "0");
+ } else {
+ f_print(fout, "sizeof ( ");
+ /* XXX: should "follow" be 1 ??? */
+ ptype(prefix, type, 0);
+ f_print(fout, ")");
+ }
+ f_print(fout, ",\n");
+}
diff --git a/usr.bin/rpcgen/rpc_util.c b/usr.bin/rpcgen/rpc_util.c
new file mode 100644
index 0000000..1ade911
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_util.c
@@ -0,0 +1,517 @@
+/*
+ * 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
+ */
+
+#ident "@(#)rpc_util.c 1.14 93/07/05 SMI"
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_util.c 1.11 89/02/22 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_util.c, Utility routines for the RPC protocol compiler
+ * Copyright (C) 1989, Sun Microsystems, Inc.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "rpc_scan.h"
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+#define ARGEXT "argument"
+
+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 7
+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 void printwhere __P(( void ));
+
+/*
+ * Reinitialize the world
+ */
+void
+reinitialize()
+{
+ memset(curline, 0, MAXLINESIZE);
+ where = curline;
+ linenum = 0;
+ defined = NULL;
+}
+
+/*
+ * string equality
+ */
+int
+streq(a, b)
+ char *a;
+ char *b;
+{
+ return (strcmp(a, b) == 0);
+}
+
+/*
+ * find a value in a list
+ */
+definition *
+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;
+ definition *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 int
+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:
+ if (streq(def->def.ty.old_type, "opaque"))
+ return ("char");
+ else
+ 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 int
+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));
+ }
+}
+
+int
+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;
+ }
+ }
+
+ return (0);
+}
+
+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_svc(pname, vnum)
+ char *pname;
+ char *vnum;
+{
+ f_print(fout, "%s_%s_svc", locase(pname), vnum);
+}
+
+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.
+ */
+void
+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 void
+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 void
+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);
+}
+
+char *
+make_argname(pname, vname)
+ char *pname;
+ char *vname;
+{
+ char *name;
+
+ name = malloc(strlen(pname) + strlen(vname) + strlen(ARGEXT) + 3);
+ if (!name) {
+ fprintf(stderr, "failed in malloc");
+ exit(1);
+ }
+ sprintf(name, "%s_%s_%s", locase(pname), vname, ARGEXT);
+ return (name);
+}
+
+bas_type *typ_list_h;
+bas_type *typ_list_t;
+
+void
+add_type(len, type)
+int len;
+char *type;
+{
+ bas_type *ptr;
+
+ if ((ptr = (bas_type *) malloc(sizeof (bas_type))) ==
+ (bas_type *)NULL) {
+ fprintf(stderr, "failed in malloc");
+ exit(1);
+ }
+
+ ptr->name = type;
+ ptr->length = len;
+ ptr->next = NULL;
+ if (typ_list_t == NULL)
+ {
+
+ typ_list_t = ptr;
+ typ_list_h = ptr;
+ }
+ else
+ {
+ typ_list_t->next = ptr;
+ typ_list_t = ptr;
+ };
+}
+
+
+bas_type *find_type(type)
+char *type;
+{
+ bas_type * ptr;
+
+ ptr = typ_list_h;
+ while (ptr != NULL)
+ {
+ if (strcmp(ptr->name, type) == 0)
+ return (ptr);
+ else
+ ptr = ptr->next;
+ };
+ return (NULL);
+}
diff --git a/usr.bin/rpcgen/rpc_util.h b/usr.bin/rpcgen/rpc_util.h
new file mode 100644
index 0000000..f465bfc
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_util.h
@@ -0,0 +1,213 @@
+/*
+ * 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
+ */
+#pragma ident "@(#)rpc_util.h 1.16 94/05/15 SMI"
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
+/* The copyright notice above does not evidence any */
+/* actual or intended publication of such source code. */
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+* PROPRIETARY NOTICE (Combined)
+*
+* This source code is unpublished proprietary information
+* constituting, or derived under license from AT&T's UNIX(r) System V.
+* In addition, portions of such source code were derived from Berkeley
+* 4.3 BSD under license from the Regents of the University of
+* California.
+*
+*
+*
+* Copyright Notice
+*
+* Notice of copyright on this source code product does not indicate
+* publication.
+*
+* (c) 1986,1987,1988.1989 Sun Microsystems, Inc
+* (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
+* All rights reserved.
+*/
+
+/* @(#)rpc_util.h 1.5 90/08/29 (C) 1987 SMI */
+
+/*
+ * rpc_util.h, Useful definitions for the RPC protocol compiler
+ */
+#include <sys/types.h>
+#include <stdlib.h>
+
+#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 {
+ definition *val;
+ struct list *next;
+};
+typedef struct list list;
+
+struct xdrfunc {
+ char *name;
+ int pointerp;
+ struct xdrfunc *next;
+};
+typedef struct xdrfunc xdrfunc;
+
+struct commandline {
+ int cflag; /* xdr C routines */
+ int hflag; /* header file */
+ int lflag; /* client side stubs */
+ int mflag; /* server side stubs */
+ int nflag; /* netid flag */
+ int sflag; /* server stubs for the given transport */
+ int tflag; /* dispatch Table file */
+ int Ssflag; /* produce server sample code */
+ int Scflag; /* produce client sample code */
+ int makefileflag; /* Generate a template Makefile */
+ char *infile; /* input module name */
+ char *outfile; /* output module name */
+};
+
+#define PUT 1
+#define GET 2
+
+/*
+ * 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;
+
+
+extern bas_type *typ_list_h;
+extern bas_type *typ_list_t;
+extern xdrfunc *xdrfunc_head, *xdrfunc_tail;
+
+/*
+ * All the option flags
+ */
+extern int inetdflag;
+extern int pmflag;
+extern int tblflag;
+extern int logflag;
+extern int newstyle;
+extern int Cflag; /* ANSI-C/C++ flag */
+extern int CCflag; /* C++ flag */
+extern int tirpcflag; /* flag for generating tirpc code */
+extern int inline; /* if this is 0, then do not generate inline code */
+extern int mtflag;
+
+/*
+ * Other flags related with inetd jumpstart.
+ */
+extern int indefinitewait;
+extern int exitnow;
+extern int timerflag;
+
+extern int nonfatalerrors;
+
+extern pid_t childpid;
+
+/*
+ * rpc_util routines
+ */
+void reinitialize();
+void crash();
+void add_type(int len, char *type);
+
+void storeval();
+
+#define STOREVAL(list,item) \
+ storeval(list,item)
+
+definition *findval();
+
+#define FINDVAL(list,item,finder) \
+ findval(list, item, finder)
+
+char *fixtype();
+char *stringfix();
+char *locase();
+void pvname_svc();
+void pvname();
+void ptype();
+int isvectordef();
+int streq();
+void error();
+void expected1();
+void expected2();
+void expected3();
+void tabify();
+void record_open();
+bas_type *find_type();
+/*
+ * rpc_cout routines
+ */
+void cprint();
+void emit();
+
+/*
+ * rpc_hout routines
+ */
+void print_datadef();
+void print_funcdef();
+void print_xdr_func_def();
+
+/*
+ * rpc_svcout routines
+ */
+void write_most();
+void write_register();
+void write_rest();
+void write_programs();
+void write_svc_aux();
+void write_inetd_register();
+void write_netid_register();
+void write_nettype_register();
+/*
+ * rpc_clntout routines
+ */
+void write_stubs();
+
+/*
+ * rpc_tblout routines
+ */
+void write_tables();
diff --git a/usr.bin/rpcgen/rpcgen.1 b/usr.bin/rpcgen/rpcgen.1
new file mode 100644
index 0000000..52d64b6
--- /dev/null
+++ b/usr.bin/rpcgen/rpcgen.1
@@ -0,0 +1,552 @@
+.\" @(#)rpcgen.1 1.35 93/06/02 SMI
+'\"macro stdmacro
+.\" Copyright 1985-1993 Sun Microsystems, Inc.
+.nr X
+.TH rpcgen 1 "28 Mar 1993"
+.SH NAME
+rpcgen \- an RPC protocol compiler
+.SH SYNOPSIS
+.BI rpcgen " infile"
+.LP
+.B rpcgen
+[
+.B \-a
+] [
+.B \-b
+] [
+.B \-C
+] [
+.BI \-D name
+[ =
+.I value
+] ]
+.if n .ti +5n
+[
+.BI \-i " size"
+]
+[
+.B \-I
+[
+.BI \-K " seconds"
+] ]
+[
+.B \-L
+] [
+.B \-M
+]
+.if n .ti +5n
+.if t .ti +5n
+[
+.B \-N
+]
+[
+.B \-T
+] [
+.BI \-Y " pathname"
+]
+.I infile
+.LP
+.B rpcgen
+[
+.B \-c
+|
+.B \-h
+|
+.B \-l
+|
+.B \-m
+|
+.B \-t
+|
+.B \-Sc
+|
+.B \-Ss
+|
+.B \-Sm
+]
+.if n .ti +5n
+[
+.BI \-o " outfile"
+] [
+.I infile
+]
+.LP
+.B rpcgen
+[
+.BI \-s " nettype"
+] [
+.BI \-o " outfile"
+] [
+.I infile
+]
+.LP
+.B rpcgen
+[
+.BI \-n " netid"
+] [
+.BI \-o " outfile"
+] [
+.I infile
+]
+.\" .SH AVAILABILITY
+.\" .LP
+.\" SUNWcsu
+.SH DESCRIPTION
+.IX "rpcgen" "" "\fLrpcgen\fP \(em RPC protocol compiler"
+.IX "RPC" "protocol compiler" "" "protocol compiler \(em \fLrpcgen\fP"
+.IX "RPC Language" "RPC protocol compiler" "" "RPC protocol compiler \(em \fLrpcgen\fP"
+.IX "compilers" "RPC protocol compiler" "" "RPC protocol compiler \(em \fLrpcgen\fP"
+.IX "programming tools" "RPC protocol compiler" "" "RPC protocol compiler \(em \fLrpcgen\fP"
+.LP
+\f3rpcgen\f1
+is a tool that generates C code to implement an RPC protocol.
+The input to
+\f3rpcgen\f1
+is a language similar to C known as
+RPC Language (Remote Procedure Call Language).
+.LP
+\f3rpcgen\f1
+is normally used as in the first synopsis where
+it takes an input file and generates three output files.
+If the
+\f2infile\f1
+is named
+\f3proto.x\f1,
+then
+\f3rpcgen\f1
+generates a header in
+\f3proto.h\f1,
+XDR routines in
+\f3proto_xdr.c\f1,
+server-side stubs in
+\f3proto_svc.c\f1,
+and client-side stubs in
+\f3proto_clnt.c\f1.
+With the
+\f3\-T\f1
+option,
+it also generates the RPC dispatch table in
+\f3proto_tbl.i\f1.
+.LP
+.B rpcgen
+can also generate sample client and server files
+that can be customized to suit a particular application. The
+\f3\-Sc\f1,
+\f3\-Ss\f1
+and
+\f3\-Sm\f1
+options generate sample client, server and makefile, respectively.
+The
+\f3\-a\f1
+option generates all files, including sample files. If the infile
+is \f3proto.x\f1, then the client side sample file is written to
+\f3proto_client.c\f1, the server side sample file to \f3proto_server.c\f1
+and the sample makefile to \f3makefile.proto\f1.
+.LP
+The server created can be started both by the port monitors
+(for example, \f3inetd\f1)
+or by itself.
+When it is started by a port monitor,
+it creates servers only for the transport for which
+the file descriptor \f30\fP was passed.
+The name of the transport must be specified
+by setting up the environment variable
+\f3PM_TRANSPORT\f1.
+When the server generated by
+\f3rpcgen\f1
+is executed,
+it creates server handles for all the transports
+specified in
+\f3NETPATH\f1
+environment variable,
+or if it is unset,
+it creates server handles for all the visible transports from
+\f3/etc/netconfig\f1
+file.
+Note:
+the transports are chosen at run time and not at compile time.
+When the server is self-started,
+it backgrounds itself by default.
+A special define symbol
+\f3RPC_SVC_FG\f1
+can be used to run the server process in foreground.
+.LP
+The second synopsis provides special features which allow
+for the creation of more sophisticated RPC servers.
+These features include support for user provided
+\f3#defines\f1
+and RPC dispatch tables.
+The entries in the RPC dispatch table contain:
+.RS
+.PD 0
+.TP 3
+\(bu
+pointers to the service routine corresponding to that procedure,
+.TP
+\(bu
+a pointer to the input and output arguments
+.TP
+\(bu
+the size of these routines
+.PD
+.RE
+A server can use the dispatch table to check authorization
+and then to execute the service routine;
+a client library may use it to deal with the details of storage
+management and XDR data conversion.
+.LP
+The other three synopses shown above are used when
+one does not want to generate all the output files,
+but only a particular one.
+See the
+.SM EXAMPLES
+section below for examples of
+.B rpcgen
+usage.
+When
+\f3rpcgen\f1
+is executed with the
+\f3\-s\f1
+option,
+it creates servers for that particular class of transports.
+When
+executed with the
+\f3\-n\f1
+option,
+it creates a server for the transport specified by
+\f2netid\f1.
+If
+\f2infile\f1
+is not specified,
+\f3rpcgen\f1
+accepts the standard input.
+.LP
+The C preprocessor,
+\f3cc \-E\f1
+is run on the input file before it is actually interpreted by
+\f3rpcgen\f1.
+For each type of output file,
+\f3rpcgen\f1
+defines a special preprocessor symbol for use by the
+\f3rpcgen\f1
+programmer:
+.LP
+.PD 0
+.RS
+.TP 12
+\f3RPC_HDR\f1
+defined when compiling into headers
+.TP
+\f3RPC_XDR\f1
+defined when compiling into XDR routines
+.TP
+\f3RPC_SVC\f1
+defined when compiling into server-side stubs
+.TP
+\f3RPC_CLNT\f1
+defined when compiling into client-side stubs
+.TP
+\f3RPC_TBL\f1
+defined when compiling into RPC dispatch tables
+.RE
+.PD
+.LP
+Any line beginning with
+``\f3%\f1''
+is passed directly into the output file,
+uninterpreted by
+\f3rpcgen\f1.
+To specify the path name of the C preprocessor use \f3\-Y\f1 flag.
+.LP
+For every data type referred to in
+\f2infile\f1,
+\f3rpcgen\f1
+assumes that there exists a
+routine with the string
+\f3xdr_\f1
+prepended to the name of the data type.
+If this routine does not exist in the RPC/XDR
+library, it must be provided.
+Providing an undefined data type
+allows customization of XDR routines.
+.br
+.ne 10
+.SH OPTIONS
+.TP 15
+\f3\-a\f1
+Generate all files, including sample files.
+.TP
+\f3\-b\f1
+Backward compatibility mode.
+Generate transport specific RPC code for older versions
+of the operating system.
+.IP
+Note: in FreeBSD, this compatibility flag is turned on by
+default since FreeBSD supports only the older ONC RPC library.
+.TP
+\f3\-c\f1
+Compile into XDR routines.
+.TP
+\f3\-C\f1
+Generate header and stub files which can be used with
+.SM ANSI C
+compilers. Headers generated with this flag can also be
+used with C++ programs.
+.TP
+\f3\-D\f2name\f3[=\f2value\f3]\f1
+Define a symbol
+\f2name\f1.
+Equivalent to the
+\f3#define\f1
+directive in the source.
+If no
+\f2value\f1
+is given,
+\f2value\f1
+is defined as \f31\f1.
+This option may be specified more than once.
+.TP
+\f3\-h\f1
+Compile into
+\f3C\f1
+data-definitions (a header).
+\f3\-T\f1
+option can be used in conjunction to produce a
+header which supports RPC dispatch tables.
+.TP
+\f3\-i \f2size\f1
+Size at which to start generating inline code.
+This option is useful for optimization. The default size is 5.
+.IP
+Note: in order to provide backwards compatibility with the older
+.B rpcgen
+on the FreeBSD platform, the default is actually 0 (which means
+that inline code generation is disabled by default). You must specify
+a non-zero value explicitly to override this default.
+.TP
+\f3\-I\f1
+Compile support for
+.BR inetd (8)
+in the server side stubs.
+Such servers can be self-started or can be started by \f3inetd\f1.
+When the server is self-started, it backgrounds itself by default.
+A special define symbol \f3RPC_SVC_FG\f1 can be used to run the
+server process in foreground, or the user may simply compile without
+the \f3\-I\f1 option.
+.br
+.ne 5
+.IP
+If there are no pending client requests, the
+\f3inetd\f1 servers exit after 120 seconds (default).
+The default can be changed with the
+.B \-K
+option.
+All the error messages for \f3inetd\f1 servers
+are always logged with
+.BR syslog (3).
+.\" .IP
+.\" Note:
+.\" this option is supported for backward compatibility only.
+.\" By default,
+.\" .B rpcgen
+.\" generates servers that can be invoked through portmonitors.
+.TP
+.BI \-K " seconds"
+By default, services created using \f3rpcgen\fP and invoked through
+port monitors wait \f3120\fP seconds
+after servicing a request before exiting.
+That interval can be changed using the \f3\-K\fP flag.
+To create a server that exits immediately upon servicing a request,
+use
+.BR "\-K\ 0".
+To create a server that never exits, the appropriate argument is
+\f3\-K \-1\fP.
+.IP
+When monitoring for a server,
+some portmonitors
+.I always
+spawn a new process in response to a service request.
+If it is known that a server will be used with such a monitor, the
+server should exit immediately on completion.
+For such servers, \f3rpcgen\fP should be used with \f3\-K 0\fP.
+.TP
+\f3\-l\f1
+Compile into client-side stubs.
+.TP
+.B \-L
+When the servers are started in foreground, use
+.BR syslog (3)
+to log the server errors instead of printing them on the standard
+error.
+.TP
+\f3\-m\f1
+Compile into server-side stubs,
+but do not generate a \(lqmain\(rq routine.
+This option is useful for doing callback-routines
+and for users who need to write their own
+\(lqmain\(rq routine to do initialization.
+.TP
+\f3\-M\f1
+Generate multithread-safe stubs for passing arguments and results between
+rpcgen generated code and user written code. This option is useful
+for users who want to use threads in their code. However, the
+.BR rpc_svc_calls (3N)
+functions are not yet MT-safe, which means that rpcgen generated server-side
+code will not be MT-safe.
+.TP
+.B \-N
+This option allows procedures to have multiple arguments.
+It also uses the style of parameter passing that closely resembles C.
+So, when passing an argument to a remote procedure, you do not have to
+pass a pointer to the argument, but can pass the argument itself.
+This behavior is different from the old style of
+.B rpcgen
+generated code.
+To maintain backward compatibility,
+this option is not the default.
+.TP
+\f3\-n \f2netid\f1
+Compile into server-side stubs for the transport
+specified by
+\f2netid\f1.
+There should be an entry for
+\f2netid\f1
+in the
+netconfig database.
+This option may be specified more than once,
+so as to compile a server that serves multiple transports.
+.TP
+\f3\-o \f2outfile\f1
+Specify the name of the output file.
+If none is specified,
+standard output is used
+(\f3\-c\f1,
+\f3\-h\f1,
+\f3\-l\f1,
+\f3\-m\f1,
+\f3\-n\f1,
+\f3\-s\f1,
+\f3\-Sc\f1,
+\f3\-Sm\f1,
+\f3\-Ss\f1,
+and
+\f3\-t\f1
+modes only).
+.TP
+\f3\-s \f2nettype\f1
+Compile into server-side stubs for all the
+transports belonging to the class
+\f2nettype\f1.
+The supported classes are
+\f3netpath\f1,
+\f3visible\f1,
+\f3circuit_n\f1,
+\f3circuit_v\f1,
+\f3datagram_n\f1,
+\f3datagram_v\f1,
+\f3tcp\f1,
+and
+\f3udp\f1
+(see
+.BR rpc (3N)
+for the meanings associated with these classes).
+This option may be specified more than once.
+Note:
+the transports are chosen at run time and not at compile time.
+.TP
+\f3\-Sc\f1
+Generate sample client code that uses remote procedure calls.
+.br
+.ne 5
+.TP
+\f3\-Sm\f1
+Generate a sample Makefile which can be used for compiling the
+application.
+.TP
+\f3\-Ss\f1
+Generate sample server code that uses remote procedure calls.
+.TP
+\f3\-t\f1
+Compile into RPC dispatch table.
+.TP
+\f3\-T\f1
+Generate the code to support RPC dispatch tables.
+.IP
+The options
+\f3\-c\f1,
+\f3\-h\f1,
+\f3\-l\f1,
+\f3\-m\f1,
+\f3\-s\f1,
+\f3\-Sc\f1,
+\f3\-Sm\f1,
+\f3\-Ss\f1,
+and
+\f3\-t\f1
+are used exclusively to generate a particular type of file,
+while the options
+\f3\-D\f1
+and
+\f3\-T\f1
+are global and can be used with the other options.
+.TP
+\f3\-Y\f2 pathname\f1
+Give the name of the directory where
+.B rpcgen
+will start looking for the C-preprocessor.
+.br
+.ne 5
+.SH EXAMPLES
+The following example:
+.LP
+.RS
+.B example% rpcgen \-T prot.x
+.RE
+.LP
+generates all the five files:
+.BR prot.h ,
+.BR prot_clnt.c ,
+.BR prot_svc.c ,
+.B prot_xdr.c
+and
+.BR prot_tbl.i .
+.LP
+The following example sends the C data-definitions (header)
+to the standard output.
+.LP
+.RS
+.B example% rpcgen \-h prot.x
+.RE
+.LP
+To send the test version of the
+.BR -DTEST ,
+server side stubs for
+all the transport belonging to the class
+.B datagram_n
+to standard output, use:
+.LP
+.RS
+.B example% rpcgen \-s datagram_n \-DTEST prot.x
+.RE
+.LP
+To create the server side stubs for the transport indicated
+by
+\f2netid\f1
+\f3tcp\f1,
+use:
+.LP
+.RS
+.B example% rpcgen \-n tcp \-o prot_svc.c prot.x
+.RE
+.SH "SEE ALSO"
+.BR cc (1),
+.BR inetd (8),
+.BR syslog (3),
+.BR rpc (3),
+.\" .BR rpc_svc_calls (3)
+.LP
+The
+.B rpcgen
+chapter in the
+.TZ NETP
+manual.
diff --git a/usr.bin/rpcinfo/Makefile b/usr.bin/rpcinfo/Makefile
new file mode 100644
index 0000000..f9377c6
--- /dev/null
+++ b/usr.bin/rpcinfo/Makefile
@@ -0,0 +1,8 @@
+# from: @(#)Makefile 5.2 (Berkeley) 5/11/90
+# $Id$
+
+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..25662e3
--- /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$
+.\"
+.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..4a50531
--- /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.4 1997/02/22 19:56:45 peter 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:")) != -1) {
+ 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..2f5e92b
--- /dev/null
+++ b/usr.bin/rs/rs.c
@@ -0,0 +1,547 @@
+/*-
+ * 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, k;
+
+ 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 = k = 0; i < orows; i++) {
+ for (j = 0; j < ocols; j++, k++)
+ if (k < nelem)
+ prints(ep[k], 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..0fab930
--- /dev/null
+++ b/usr.bin/rsh/rsh.1
@@ -0,0 +1,193 @@
+.\" 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 t Ar timeout
+.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.
+.It Fl t
+The
+.Fl t
+option allows a timeout to be specified (in seconds). If no
+data is sent or received in this time, rsh will exit.
+.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 setsockopt 2 ,
+.Xr kerberos 3 ,
+.Xr krb_realmofhost 3 ,
+.Xr krb_sendauth 3 ,
+.Xr rcmd 3 ,
+.Xr ruserok 3 ,
+.Xr hosts 5 ,
+.Xr rlogind 8 ,
+.Xr rshd 8
+.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..70f311d
--- /dev/null
+++ b/usr.bin/rsh/rsh.c
@@ -0,0 +1,496 @@
+/*-
+ * 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.10 1997/02/22 19:56:46 peter 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 <sys/time.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 <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, 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;
+ int timeout = 0;
+
+ 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:nt:wx"
+#else
+#define OPTIONS "8KLde:k:l:nt:w"
+#endif
+#else
+#define OPTIONS "8KLde:l:nt:w"
+#endif
+ while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
+ 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 't':
+ timeout = atoi(optarg);
+ break;
+ 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_krb(&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, timeout);
+
+ if (!nflag)
+ (void)kill(pid, SIGKILL);
+ exit(0);
+}
+
+void
+talk(nflag, omask, pid, rem, timeout)
+ int nflag;
+ long omask;
+ pid_t pid;
+ int rem;
+{
+ int cc, wc;
+ fd_set readfrom, ready, rembits;
+ char *bp, buf[BUFSIZ];
+ struct timeval tvtimeout;
+ int srval;
+
+ 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);
+ }
+
+ tvtimeout.tv_sec = timeout;
+ tvtimeout.tv_usec = 0;
+
+ (void)sigsetmask(omask);
+ FD_ZERO(&readfrom);
+ FD_SET(rfd2, &readfrom);
+ FD_SET(rem, &readfrom);
+ do {
+ ready = readfrom;
+ if (timeout) {
+ srval = select(16, &ready, 0, 0, &tvtimeout);
+ } else {
+ srval = select(16, &ready, 0, 0, 0);
+ }
+
+ if (srval < 0) {
+ if (errno != EINTR)
+ err(1, "select");
+ continue;
+ }
+ if (srval == 0)
+ errx(1, "timeout reached (%d seconds)\n", timeout);
+ 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] [-t timeout] 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..ab50a8a
--- /dev/null
+++ b/usr.bin/rup/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+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..76e6c1f
--- /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$
+.\"
+.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 accommodate 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..ba9a67e
--- /dev/null
+++ b/usr.bin/rup/rup.c
@@ -0,0 +1,231 @@
+/*-
+ * 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$";
+#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;
+
+ #define updays (host_stat->curtime.tv_sec / 86400)
+ if (host_uptime.tm_yday != 0)
+ sprintf(days_buf, "%3d day%s, ", updays,
+ (updays > 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;
+ struct timeval tv;
+
+ 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));
+ tv.tv_sec = 15; /* XXX ??? */
+ tv.tv_usec = 0;
+ if (clnt_call(rstat_clnt, RSTATPROC_STATS, xdr_void, NULL, xdr_statstime, &host_stat, tv) != 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..3e48bce
--- /dev/null
+++ b/usr.bin/ruptime/ruptime.1
@@ -0,0 +1,82 @@
+.\" 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
+.\" $Id$
+.\"
+.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..e075d92
--- /dev/null
+++ b/usr.bin/ruptime/ruptime.c
@@ -0,0 +1,284 @@
+/*
+ * 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.10 1997/03/29 04:32:02 imp 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 LEFTEARTH(h) (now - (h)->hs_wd->wd_recvtime > 4*24*60*60)
+#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")) != -1)
+ 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 (LEFTEARTH(hsp))
+ continue;
+ 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 %3d+%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..a6b753f
--- /dev/null
+++ b/usr.bin/rusers/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+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..7cf6164
--- /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$
+.\"
+.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 accommodate any RPC-based services. The host may be down.
+.El
+.Sh SEE ALSO
+.Xr who 1 ,
+.Xr portmap 8 ,
+.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..3d7b9a5
--- /dev/null
+++ b/usr.bin/rusers/rusers.c
@@ -0,0 +1,252 @@
+/*-
+ * 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$";
+#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;
+ struct timeval tv;
+
+ 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));
+ tv.tv_sec = 15; /* XXX ?? */
+ tv.tv_usec = 0;
+ if (clnt_call(rusers_clnt, RUSERSPROC_NAMES, xdr_void, NULL, xdr_utmpidlearr, &up, tv) != 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..e81c30a
--- /dev/null
+++ b/usr.bin/rwall/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+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..1531503
--- /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$
+.\"
+.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 who 1 ,
+.Xr portmap 8 ,
+.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..fe0983a
--- /dev/null
+++ b/usr.bin/rwall/rwall.c
@@ -0,0 +1,177 @@
+/*
+ * 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$";
+#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;
+ struct timeval tv;
+
+ 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);
+ }
+
+ tv.tv_sec = 15; /* XXX ?? */
+ tv.tv_usec = 0;
+ if (clnt_call(cl, WALLPROC_WALL, xdr_wrapstring, &mbuf, xdr_void, &res, tv) != 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..898ccc7
--- /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/whod.* -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..557e8fa
--- /dev/null
+++ b/usr.bin/rwho/rwho.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[] = "@(#)rwho.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <protocols/rwhod.h>
+#include <dirent.h>
+#include <locale.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <utmp.h>
+
+DIR *dirp;
+
+struct whod wd;
+int utmpcmp();
+#define NUSERS 1000
+struct myutmp {
+ char myhost[sizeof(wd.wd_hostname)];
+ 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 dirent *dp;
+ int cc, width;
+ register struct whod *w = &wd;
+ register struct whoent *we;
+ register struct myutmp *mp;
+ int f, n, i;
+
+ (void) setlocale(LC_TIME, "");
+
+ while ((ch = getopt(argc, argv, "a")) != -1)
+ 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 + sizeof(mp->myutmp.out_line);
+ 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:%-.*s", mp->myhost,
+ sizeof(mp->myutmp.out_line), mp->myutmp.out_line);
+ printf("%-*.*s %-*s %.12s",
+ UT_NAMESIZE, sizeof(mp->myutmp.out_name),
+ 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, sizeof(u2->myutmp.out_name));
+ if (rc)
+ return (rc);
+ rc = strcmp(u1->myhost, u2->myhost);
+ if (rc)
+ return (rc);
+ return (strncmp(u1->myutmp.out_line, u2->myutmp.out_line, sizeof(u2->myutmp.out_line)));
+}
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..ab2275f
--- /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 behavior 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 settings.
+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..b968f9e
--- /dev/null
+++ b/usr.bin/sasc/sasc.c
@@ -0,0 +1,197 @@
+/* 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: sasc.c,v 1.4 1997/02/22 19:56:57 peter Exp $
+ */
+
+#include <stdlib.h>
+#include <unistd.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..4e8ebba
--- /dev/null
+++ b/usr.bin/sccs/PSD.doc/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.2 (Berkeley) 5/23/94
+
+DIR= psd/14.sccs
+SRCS= sccs.me
+MACROS= -me
+
+.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..16dc3fb
--- /dev/null
+++ b/usr.bin/sccs/PSD.doc/sccs.me
@@ -0,0 +1,1609 @@
+.\" 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.2 (Berkeley) 6/1/94
+.\"
+.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
+.ne 1i
+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..6c9ed08
--- /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;
+
+void done __P((void)) __dead2;
+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")) != -1)
+ 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..71c7f20
--- /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/bin/sed
+ BASELOG=sed.out
+ TEST=`cd ..; make whereobj`/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..91bf843
--- /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 && isspace((unsigned char)*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, esc_nl, 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 (esc_nl = 0; *p != '\0'; p++) {
+ if (*p == '\\' && p[1] != '\0' && *++p == '\n')
+ esc_nl = 1;
+ *s++ = *p;
+ }
+ size += s - op;
+ if (!esc_nl) {
+ *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((unsigned char)*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..02cbfac
--- /dev/null
+++ b/usr.bin/sed/main.c
@@ -0,0 +1,355 @@
+/*-
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <locale.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;
+
+ (void) setlocale(LC_ALL, "");
+
+ fflag = 0;
+ while ((c = getopt(argc, argv, "ae:f:n")) != -1)
+ 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 *p;
+ int c;
+
+ 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..3b0828c
--- /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 (isprint((unsigned char)*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((unsigned char)*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..a28efdb
--- /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\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..12e52d5
--- /dev/null
+++ b/usr.bin/sgmlfmt/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+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..9b9016e
--- /dev/null
+++ b/usr.bin/sgmlfmt/sgmlfmt.1
@@ -0,0 +1,172 @@
+.Dd May 9, 1997
+.Os FreeBSD 2.2
+.Dt SGMLFMT 1
+.Sh NAME
+.Nm sgmlfmt
+.Nd Formats linuxdoc and DocBook SGML documents.
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar doctype
+.Fl f Ar format
+.Op Fl i Ar name ...
+.Op Fl links
+.Op Fl e Ar encoding
+.Op Fl hdr Ar file
+.Op Fl ftr Ar file
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+command reads SGML files tagged according to the linuxdoc or
+DocBook DTD,
+validates them using the
+.Xr sgmls 1
+parser and then converts them to the specified output format.
+Linuxdoc input files 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 d Ar doctype
+Specifies the SGML document type of the source file. Currently
+supported document types are
+.Ar linuxdoc
+and
+.Ar docbook .
+If no
+.Fl d
+option is specified, the default is
+.Ar linuxdoc .
+.It Fl f Ar format
+Determines the output format which can be one of the following:
+.Bl -tag -width Ds
+.It Ar ascii
+Generates a single output file with the extension
+.Pa .ascii
+suitable for viewing on an ASCII terminal.
+.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 koi8-r
+Generates a single output file with the extension
+.Pa .koi8-r
+suitable for viewing on an terminal supporting the KOI8-R
+character encoding.
+.It Ar latin1
+Generates a single output file with the extension
+.Pa .latin1
+suitable for viewing on an terminal supporting the ISO8859-1
+character encoding.
+.It Ar ps
+Generates a single output file with the extension
+.Pa .ps
+suitable for printing or display on a PostScript compatible device.
+.It Ar roff
+Generates a single output file with the extension
+.Pa .roff
+suitable processing with
+.Xr groff 1 .
+This is actually an intermediate conversion used by the
+.Fl f Ar ascii ,
+.Fl f Ar latin1 ,
+.Fl f Ar koi8-r ,
+and
+.Fl f Ar ps
+format options.
+.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, this option generates a shell script named
+.Pa file.ln .
+For each
+.Li <label id="foo">
+in the document source,
+.Pa file.ln
+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 occurrence of a slash (/) in label
+is replaced with percent (%), while any occurrence of a space is replaced
+with an underscore (_).
+.It Fl e Ar encoding
+When used with the
+.Fl f Ar html
+option, specifies document encoding to use for the generated
+HTML files. If this option is not specified, iso-8859-1 is used.
+.It Fl hdr Ar file
+When used with the
+.Fl f Ar html
+option, the specified file will be inserted at the beginning of the
+body element of each generated HTML file.
+.It Fl ftr Ar file
+When used with the
+.Fl f Ar html
+option, the specified file will be inserted at the end of the
+body element of each generated HTML file.
+.El
+.Pp
+In all cases, the output files are created in the current working
+directory.
+.Sh FILES
+.Pa /usr/share/sgml/transpec
+- directory containing translation specification files for
+.Xr instant 1 .
+.Pp
+.Sh SEE ALSO
+.Xr groff 1 ,
+.Xr instant 1 ,
+.Xr sgmls 1 ,
+.Xr transpec 5
+.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 .
diff --git a/usr.bin/sgmlfmt/sgmlfmt.pl b/usr.bin/sgmlfmt/sgmlfmt.pl
new file mode 100755
index 0000000..5f9ab85
--- /dev/null
+++ b/usr.bin/sgmlfmt/sgmlfmt.pl
@@ -0,0 +1,749 @@
+#!/usr/bin/perl
+
+# $Id: sgmlfmt.pl,v 1.25 1997/05/10 20:41:11 jfieber Exp $
+
+# Copyright (C) 1996
+# John R. Fieber. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 JOHN R. FIEBER AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL JOHN R. FIEBER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+
+# Format an sgml document tagged according to the linuxdoc DTD.
+# by John Fieber <jfieber@freebsd.org> for the FreeBSD documentation
+# project.
+
+
+require 'newgetopt.pl';
+
+#
+# The SGML parser, and translation engine.
+#
+
+$sgmls = "sgmls";
+$instant = "instant";
+
+#
+# Things to clean up if we exit abnormally
+#
+
+@cleanfiles = ();
+
+#
+# Interrupt handler, remove scratch files.
+#
+
+sub sighandler {
+ local($sig) = @_;
+ unlink @cleanfiles;
+ exit(1);
+}
+
+$SIG{'HUP'} = 'sighandler';
+$SIG{'INT'} = 'sighandler';
+$SIG{'QUIT'} = 'sighandler';
+
+#
+# Display a usage message.
+#
+
+sub usage {
+ print "Usage:\n";
+ print "sgmlfmt [-d <doctype>] -f <format> [-i <namea> ...] [-links]\n";
+ print " [-e encoding] [-hdr file] [-ftr file] file\n";
+ print "where <doctype> is one of: linuxdoc (default), docbook.\n";
+ print "and <format> is one of: ascii, html, koi8-r, latin1, ps, roff\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/\.[^\.]*$//; # drop the extension
+ $filepath = $file;
+ $filepath =~ s/\/*[^\/]*$//;
+ if ($filepath ne "") {
+ $ENV{"SGML_PATH"} .= ":$filepath/%S:%S";
+ }
+ return 1;
+}
+
+#
+# A function to run sgmls and instant on the input file.
+#
+# Arguments:
+# 1. A file handle for the output
+# 2. A translation file
+#
+
+sub sgmlparse {
+ local($ifhandle, $replacement) = @_;
+ $defines = join(" -i ", @opt_i);
+ if ($defines ne "") {
+ $defines = "-i $defines";
+ }
+ open($ifhandle, "$sgmls $defines $decl $file | " .
+ "$instant -Dfilename=$fileroot $instantopts -t ${dtd}-${replacement}.ts |");
+}
+
+#
+# Generate roff output
+#
+
+sub gen_roff {
+ @cleanfiles = (@cleanfiles, "${fileroot}.roff");
+ open (outfile, ">$fileroot.roff");
+ &sgmlparse(infile, "roff");
+ while (<infile>) {
+ print outfile;
+ }
+ close(infile);
+ close(outfile);
+}
+
+#
+# Generate something from roff output
+#
+
+sub do_groff {
+ local($driver, $postproc) = @_;
+ @cleanfiles = (@cleanfiles, "${fileroot}.trf", "${fileroot}.tmp",
+ "${fileroot}.qrf", "${fileroot}.${driver}");
+ open (outfile, ">$fileroot.trf");
+ &sgmlparse(infile, "roff");
+ while (<infile>) {
+ print outfile;
+ }
+ close(infile);
+ close(outfile);
+ system("groff -T ${driver} -t ${fileroot}.trf ${postproc} > ${fileroot}.${driver}");
+
+ # If foo.tmp has been created, then there are cross references
+ # in the file and we need a second pass to resolve them correctly.
+
+ if (stat("${fileroot}.tmp")) {
+ system("groff -T ${driver} -t ${fileroot}.trf ${postproc} > ${fileroot}.${driver}");
+ unlink("${fileroot}.qrf");
+ }
+ unlink("${fileroot}.trf");
+}
+
+#
+# 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
+
+$doctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">";
+$BODY = "<BODY text=\"#000000\" bgcolor=\"#ffffff\">";
+
+# 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);
+
+ # Remove any lingering link file
+ unlink("${fileroot}.ln");
+
+ $tmpfile = "/tmp/sgmlf.$$";
+ @cleanfiles = (@cleanfiles, "$tmpfile", "${fileroot}.html",
+ "${fileroot}_toc.html", "${fileroot}.ln");
+ 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;
+ @cleanfiles = (@cleanfiles, "${fileroot}${filecount}.html");
+ }
+ 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 "$doctype\n<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;
+ $t = $st_header[0];
+ $t =~ s|<[a-zA-Z/][^>]*>||g;
+ print tocfile "<HEAD>\n$html_encoding\n<TITLE>$t</TITLE>\n" .
+ "</HEAD>\n";
+ print tocfile "<H1>$st_header[0]</H1>\n";
+
+ $header[$st_ol[$sc]] =
+ "$doctype\n<HTML>\n<HEAD>\n$html_encoding\n<TITLE>$t</TITLE>\n" .
+ "</HEAD>\n$BODY\n";
+ $header[$st_ol[$sc]] .= $html_header;
+ $header[$st_ol[$sc]] .= "\n<H1>$st_header[0]</H1>\n";
+
+ $footer[$st_ol[$sc]] = "\n";
+ $footer[$st_ol[$sc]] .= $html_footer;
+ $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) {
+ $t = $_;
+ $t =~ s|<[a-zA-Z/][^>]*>||g;
+ $header[$st_ol[$sc]] =
+ "$doctype\n<HTML>\n<HEAD>\n$html_encoding\n<TITLE>$t</TITLE>\n" .
+ "</HEAD>\n$BODY\n";
+ $header[$st_ol[$sc]] .= $html_header;
+ $header[$st_ol[$sc]] .= "\n$navbar[$st_ol[$sc]]\n<HR NOSHADE>\n";
+ $footer[$st_ol[$sc]] = "<HR NOSHADE>\n$navbar[$st_ol[$sc]]\n";
+ $footer[$st_ol[$sc]] .= $html_footer;
+ $footer[$st_ol[$sc]] .= "\n</BODY>\n</HTML>\n";
+ }
+
+ # 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>\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";
+ if ($st_sl[$lsc] > 1) {
+ print tocfile "</DD>\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";
+ last tagsw;
+ }
+
+ # If nothing else did anything with this line, just print it.
+ $text[$st_ol[$sc]] .= "$_";
+ }
+ }
+
+ print tocfile "</HTML>\n";
+ 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";
+
+}
+
+#
+# Generate html output from docbook input
+#
+
+sub docbook_html {
+ @cleanfiles = (@cleanfiles, "${fileroot}.html");
+ open (outfile, ">$fileroot.html");
+ &sgmlparse(infile, "html");
+ while (<infile>) {
+ print outfile;
+ }
+ close(infile);
+ close(outfile);
+}
+
+# extlink
+#
+# Build a shell script to create symbolic links 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 (!fileno(LINKFILE)) {
+ open(LINKFILE, ">${fileroot}.ln");
+ }
+
+ print LINKFILE "ln -fs $fn $file\n";
+}
+
+# Now, read the command line and take appropriate action
+
+sub main {
+ # Check arguments
+ if (!&NGetOpt('d=s', 'f=s', 'links', 'i:s@', 'hdr=s', 'ftr=s', 'e=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;
+ }
+
+ # Figure out which DTD we are using
+ if ($opt_d eq "docbook") {
+ $dtd = "docbook";
+ $decl = "/usr/share/sgml/docbook/docbook.dcl";
+ }
+ else {
+ $dtd = "linuxdoc";
+ $decl = "/usr/share/sgml/FreeBSD/linuxdoc.dcl";
+ }
+
+ # Generate the output
+ if ($opt_f eq 'html') {
+ # Set the character encoding
+ if (! $opt_e) {
+ $opt_e = "iso-8859-1";
+ }
+ $html_encoding = "<META HTTP-EQUIV=\"Content-Type\" " .
+ "CONTENT=\"text/html; charset=" . $opt_e . "\">";
+
+ if ($dtd eq "docbook") {
+ if ($opt_hdr) {$instantopts .= " -D \"inchdr=${opt_hdr}\"";}
+ if ($opt_ftr) {$instantopts .= " -D \"incftr=${opt_ftr}\"";}
+ &docbook_html();
+ }
+ else {
+ if ($opt_hdr) {$html_header = &gethf($opt_hdr);}
+ if ($opt_ftr) {$html_footer = &gethf($opt_ftr);}
+ &gen_html();
+ }
+ }
+ elsif ($opt_f eq 'roff') {
+ &gen_roff();
+ }
+ elsif ($opt_f eq 'ascii') {
+ &do_groff("ascii", "| col");
+ }
+ elsif ($opt_f eq 'latin1') {
+ &do_groff("latin1", "| col");
+ }
+ elsif ($opt_f eq 'koi8-r') {
+ &do_groff("koi8-r", "| col");
+ }
+ elsif ($opt_f eq 'ps') {
+ &do_groff("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;
+
+sub getdate {
+ @months = ("January", "February", "March", "April", "May","June",
+ "July", "August", "September", "October", "November", "December");
+ ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+ $year += 1900;
+ return "$months[$mon] $mday, $year";
+}
+
+sub gethf {
+ local ($file) = @_;
+
+ $date = &getdate;
+ $data = "";
+
+ if (open(IN, $file)) {
+ while (<IN>) {
+ s/\@\@UPDATE\@\@/Updated $date/;
+ $data .= $_;
+ }
+ close(IN);
+ }
+ return $data;
+}
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..96ce820
--- /dev/null
+++ b/usr.bin/sgmls/Makefile
@@ -0,0 +1,9 @@
+#
+# Bmake file for sgmls
+# $Id$
+#
+
+SUBDIR= libsgmls sgmls instant
+
+.include <bsd.subdir.mk>
+
diff --git a/usr.bin/sgmls/Makefile.inc b/usr.bin/sgmls/Makefile.inc
new file mode 100644
index 0000000..7a78864
--- /dev/null
+++ b/usr.bin/sgmls/Makefile.inc
@@ -0,0 +1,16 @@
+#
+# Bmakefile for rast
+#
+# $Id$
+#
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
+
+.if exists(${.OBJDIR}/../libsgmls)
+LIBSGMLS= ${.OBJDIR}/../libsgmls/libsgmls.a
+.else
+LIBSGMLS= ${.CURDIR}/../libsgmls/libsgmls.a
+.endif
+
diff --git a/usr.bin/sgmls/README b/usr.bin/sgmls/README
new file mode 100644
index 0000000..e2a8204
--- /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/instant/Makefile b/usr.bin/sgmls/instant/Makefile
new file mode 100644
index 0000000..c004f87
--- /dev/null
+++ b/usr.bin/sgmls/instant/Makefile
@@ -0,0 +1,15 @@
+# $Id$
+
+PROG= instant
+SRCS= browse.c info.c main.c tables.c traninit.c translate.c
+SRCS+= tranvar.c util.c
+
+CFLAGS+= -I${.CURDIR}/../libsgmls -I${.CURDIR}/../sgmls
+
+LDADD= ${LIBSGMLS} -lcompat
+DPADD= ${LIBSGMLS} ${LIBCOMPAT}
+
+MAN1= instant.1
+MAN5= transpec.5
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/sgmls/instant/README b/usr.bin/sgmls/instant/README
new file mode 100644
index 0000000..d04547c
--- /dev/null
+++ b/usr.bin/sgmls/instant/README
@@ -0,0 +1,150 @@
+#
+# Copyright (c) 1994
+# Open Software Foundation, Inc.
+#
+# Permission is hereby granted to use, copy, modify and freely distribute
+# the software in this file and its documentation for any purpose without
+# fee, provided that the above copyright notice appears in all copies and
+# that both the copyright notice and this permission notice appear in
+# supporting documentation. Further, provided that the name of Open
+# Software Foundation, Inc. ("OSF") not be used in advertising or
+# publicity pertaining to distribution of the software without prior
+# written permission from OSF. OSF makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+
+ instant - a formatting application for OSF SGML instances
+____________________________________________________________________________
+
+Requirements
+
+ ANSI C compiler (gcc is one)
+
+ sgmls 1.1 -- sgml parser from James Clark. Based on Goldfarb's ARC parser.
+
+ Vanilla unix make
+
+ POSIX C libraries
+
+
+Files for instant program
+
+ Module Function
+ ------ --------
+ browse.c interactive browser
+ general.h general definitions
+ info.c print information about the instances
+ main.c main entry, arg parsing, instance reading
+ tables.c table-specific formatting routines (TeX and tbl)
+ traninit.c translator initialization (read spec, etc.)
+ translate.c main translator
+ translate.h structure definitions for translation code
+ tranvar.c routines for handling "special variables"
+ util.c general utilities
+
+
+Also required
+
+ 1. Translation spec (transpec) files. (../transpecs/*.ts)
+ 2. SDATA mapping files for mapping sdata entities. (../transpecs/*.sdata)
+ 3. Character mapping files for mapping characters. (../transpecs/*.cmap)
+
+
+Platforms tried on
+
+ OSF1 1.3 (i486)
+ Ultrix 4.2 (mips)
+ HP-UX 9.01 (hp 9000/700)
+ AIX 3.2 (rs6000)
+ SunOS [missing strerror()]
+
+____________________________________________________________________________
+
+ General outline of program
+ ------- ------- -- -------
+
+To summarize in a sentence, instant reads the output of sgmls, builds a tree
+of the instnace in memory, then traverses the tree in in-order, processing
+the nodes according to a translation spec.
+
+Element tree storage
+------- ---- -------
+
+The first thing instant must do is read the ESIS (output of sgmls) from the
+specified file or stdin, forming a tree in memory. (Nothing makes sense
+without an instance...) Each element of the instance is a node in the tree,
+stored as a structure called Element_t. Elements contain content (what
+else?), which is a mixture of data (#PCDATA, #CDATA, #RCDATA - all the same
+in the ESIS), child elements, and PIs. Each 'chunk' of content is referred
+to by a Content_t structure. A Content_t contains an enum that can point to
+a string (data or PI), another Element_t. For example, if a <p> element
+contains some characters, an <emphasis> element, some more characters,
+a <function> element, then some more characters, it has 5 Content_t children
+as an array.
+
+Element_t's have pointers to their parents, and a next element in a linked
+list (they're stored as a linked list, for cases when you'd want to quickly
+travers all the nodes, in no particular order).
+For convenience, Element_t's have an array of pointers to it's child
+Element_t's. These are just pointers to the same thing contained in the
+Content_t array, without the intervening data or PIs. This makes it easier
+for the program to traverse the elements of the tree (it does not have to
+be concerned with skipping data, etc.). There is an analagous array of
+pointers for the data content, an array of (char *)'s. This makes it easier
+to consider the immediate character content of an element.
+
+Attributes are kept as an array of name-value mappings (using the typedef
+Mapping_t). ID attributes are also stored in a separate list of ID value -
+element pointer pairs so that it is quick to find an element by ID.
+
+Other information kept about each element (in the Element_t struct) includes
+the line number in the EISI where the element occurs, the input filename.
+(These depend on sgmls being run with the "-l" option.) Also stored is
+an element's order in its parent's collection of children and an element's
+depth in the tree.
+
+Translation specs
+----------- -----
+
+A translation spec is read into a linked list in memory. As the instance
+tree is traversed, the transpecs are searched in order for a match. As a
+rule, one should position the less specific transpecs later in the file.
+Also, specs for seldom-used element are best placed later in the file, since
+it takes cpu cycles to skip over them for each of the more-used elements.
+
+During translation of a particular element, the list of Content_t structures
+are processed in order. If a content 'chunk' is data, it is printed to
+the output stream. If it is an element, the translation routine is called
+for that elemen, recursively. Hence, in-order traversal.
+
+Miscellaneous information displays
+------------- ----------- --------
+
+There are several informational display options available. They include:
+ - a list of element usage (-u) -- lists each element in the instance,
+ it's attributes, number of children, parent, data content 'nodes'.
+ - statistics about elements (-S) -- lists elements, number of times
+ each is used, percent of elements that this is, total char content
+ in that element, average number of characters in they element.
+ - show context of each element (-x) -- lists each element and its
+ context, looking up the tree (this is the same context that
+ would match the Context field of a transpec).
+ - show the hierarchy of the instance (-h) -- show an ascii 'tree' of
+ the instance, where elements deeper in the tree are indented more.
+ Numbers after the element name in parentheses are the number of
+ element content nodes and the number of data content nodes.
+
+Interactive browser
+----------- -------
+
+Originally, the interactive browser was intended as a debugging aid for
+the code developer. It was a way to examine a particular node of the
+instance tree or process a subtree without being distracted by the rest
+of the instance. Many of the commands test functionality of the query
+and search code (such as testing whether a certain element has a given
+relationship to the current position in the tree).
+
+
+____________________________________________________________________________
+
diff --git a/usr.bin/sgmls/instant/browse.c b/usr.bin/sgmls/instant/browse.c
new file mode 100644
index 0000000..c904476
--- /dev/null
+++ b/usr.bin/sgmls/instant/browse.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 1994
+ * Open Software Foundation, Inc.
+ *
+ * Permission is hereby granted to use, copy, modify and freely distribute
+ * the software in this file and its documentation for any purpose without
+ * fee, provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation. Further, provided that the name of Open
+ * Software Foundation, Inc. ("OSF") not be used in advertising or
+ * publicity pertaining to distribution of the software without prior
+ * written permission from OSF. OSF makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Copyright (c) 1996 X Consortium
+ * Copyright (c) 1995, 1996 Dalrymple Consulting
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the X Consortium and
+ * Dalrymple Consulting shall not be used in advertising or otherwise to
+ * promote the sale, use or other dealings in this Software without prior
+ * written authorization.
+ */
+
+/* ________________________________________________________________________
+ *
+ * Module for interactive browsing.
+ *
+ * Entry points for this module:
+ * Browse() interactive browser
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/browse.c,v 1.2 1996/06/02 21:46:10 fld Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "general.h"
+
+static void PrElemPlusID(Element_t *);
+static void ls_node(Element_t *, int, char **);
+static void do_query(Element_t *, char *, char *);
+static void do_find(Element_t *, char **);
+
+/* ______________________________________________________________________ */
+
+static char *br_help_msg[] = {
+ " ls List info about current element in tree",
+ " (context, children, attributes, etc.)",
+ " cd N ... Change to Nth elememt child, where N is shown by 'ls'.",
+ " N may also be '/' (top) or '..' (up).",
+ " cd id I Change to elememt whose ID is I",
+ " data N Show data of Nth data node",
+ " where Show current position in the tree",
+ " id I Show path to element with id I",
+ " (using '?' for I will lists all IDs and their paths)",
+ " find S Find elements matching spec S. Recognized syntaxes:",
+ " find attr <name> <value>",
+ " find cont <string>",
+ " find parent <gi-name>",
+ " find child <gi-name>",
+ " find gi <gi-name>",
+ " q rel gi Query: report if elem 'gi' has relation to current elem",
+ " ('rel' is one of 'child parent ancestor descendant",
+ " sibling sibling+ sibling+1 sibling- sibling-1 cousin')",
+ "",
+ " tran [outfile]",
+ " Translate into 'outfile' (stdout)",
+ " stat Print statistics (how often elements occur, etc.)",
+ " sum Print elem usage summary (# of children, depth, etc.)",
+ " tree Print document hierarchy as a tree",
+ " cont Print context of each element",
+ NULL
+};
+
+/* ______________________________________________________________________ */
+
+void
+Browse()
+{
+ char buf[256], *cmd, **av, **sv;
+ char *Prompt;
+ Element_t *ce; /* current element */
+ Element_t *e;
+ int i, n, ac;
+
+ if (slave) Prompt = "=>\n";
+ else Prompt = "=> ";
+
+ ce = DocTree;
+ while (fputs(Prompt, stdout)) {
+ if (!fgets(buf, 256, stdin)) break;
+ stripNL(buf);
+ if (buf[0] == EOS) {
+ fputs(Prompt, stdout);
+ continue;
+ }
+ ac = 20;
+ av = Split(buf, &ac, S_ALVEC);
+ if (ac > 0) cmd = av[0];
+ if (!cmd || !(*cmd)) continue;
+
+ if (!strcmp(cmd, "ls")) ls_node(ce, ac, av);
+
+ else if (!strcmp(cmd, "cd")) {
+ if (av[1]) {
+ if (ac == 3 && !strcmp(av[1], "id")) {
+ if ((e = FindElemByID(av[2]))) ce = e;
+ else printf("Element with ID '%s' not found.\n", av[2]);
+ continue;
+ }
+ for (i=1; i<ac; i++) {
+ if (!strcmp(av[i], "..")) {
+ if (ce->parent) ce = ce->parent;
+ continue;
+ }
+ if (!strcmp(av[i], "/")) {
+ if (ce->parent) ce = DocTree;
+ continue;
+ }
+ if (!isdigit(*av[i])) {
+ printf("Expecting digit, '..', or '/', got '%s'.\n",
+ av[i]);
+ break;
+ }
+ n = atoi(av[i]);
+ if (n < ce->necont) ce = ce->econt[n];
+ else {
+ printf("Must be in range 0 - %d.\n", ce->necont);
+ break;
+ }
+ }
+ }
+ }
+
+ else if (!strcmp(cmd, "data")) {
+ if (av[1] && isdigit(*av[1])) {
+ n = atoi(av[1]);
+ if (n < ce->ndcont) {
+ printf(ce->dcont[n]);
+ fputs("\n", stdout);
+ }
+ else if (ce->ndcont == 0)
+ printf("No data at this node.\n");
+ else printf("Must be in range 0 - %d.\n", ce->ndcont);
+ }
+ }
+
+ /* show where we are in the tree */
+ else if (!strcmp(cmd, "where")) PrintLocation(ce, stdout);
+
+ /* show where we are in the tree */
+ else if (!strcmp(cmd, "pwd")) PrElemPlusID(ce);
+
+ /* perform query with yes/no answer */
+ else if (!strcmp(cmd, "q") && av[1] && av[2])
+ do_query(ce, av[1], av[2]);
+
+ /* perform query printing paths to matching elements */
+ else if (!strcmp(cmd, "find") && av[1] && av[2])
+ do_find(ce, av);
+
+ /* list locations where specified ID(s) occur */
+ else if (!strcmp(cmd, "id")) {
+ if (ac <= 1) continue;
+ if (*av[1] == '?') PrintIDList();
+ else {
+ /* short: "id i1 i2 ...", long: "id -l i1 i2 ..." */
+ if (!strcmp(av[1], "-l")) n = 2;
+ else n = 1;
+ for (i=n; i<ac; i++) {
+ if ((e = FindElemByID(av[i]))) {
+ if (n == 2) { /* long (multiline) format */
+ if (n != i) putchar('\n');
+ PrintLocation(e, stdout);
+ }
+ else PrElemPlusID(e);
+ }
+ else printf("Element with ID '%s' not found.\n", av[i]);
+ }
+ }
+ }
+
+ /* show and set variables */
+ else if (!strcmp(cmd, "show") && av[1]) {
+ printf("%s\n", FindMappingVal(Variables, av[1]));
+ }
+ else if (!strcmp(cmd, "set") && av[1] && av[2]) {
+ SetMappingNV(Variables, av[1], av[2]);
+ }
+
+ /* print summary of tag usage */
+ else if (!strcmp(cmd, "sum")) {
+ if (ac > 1) PrintElemSummary(ce);
+ else PrintElemSummary(DocTree);
+ }
+ /* print element tree */
+ else if (!strcmp(cmd, "tree")) {
+ if (ac > 1) PrintElemTree(ce);
+ else PrintElemTree(DocTree);
+ }
+ /* print statistics */
+ else if (!strcmp(cmd, "stat")) {
+ if (ac > 1) PrintStats(ce);
+ else PrintStats(DocTree);
+ }
+ /* print context of each element of tree */
+ else if (!strcmp(cmd, "cont")) {
+ if (ac > 1) PrintContext(ce);
+ else PrintContext(DocTree);
+ }
+ /* print translation, given transpec */
+ else if (!strcmp(cmd, "tran")) {
+ FILE *fp;
+ if (ac > 1){
+ if (!(fp = fopen(av[1], "w"))) {
+ perror("Can not open output file");
+ continue;
+ }
+ }
+ else fp = stdout;
+ DoTranslate(ce, fp);
+ if (ac > 1) fclose(fp);
+ }
+
+ else if (!strcmp(cmd, "help") || *cmd == '?') {
+ sv = br_help_msg;
+ while (*sv) puts(*sv++);
+ }
+
+ /* quit (control-D also works) */
+ else if (!strcmp(cmd, "quit")) break;
+
+ else
+ fprintf(stderr, "Unknown command '%s' - ingored.\n", cmd);
+ }
+ putc(NL, stdout);
+}
+
+/* ______________________________________________________________________ */
+/* Do the "ls" command.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Arg count from command line (this command, not the shell command).
+ * Arg vector.
+ */
+
+static void
+ls_node(
+ Element_t *e,
+ int ac,
+ char **av
+)
+{
+ int i;
+ char buf[LINESIZE];
+
+ if (ac > 1 && !strcmp(av[1], "-n")) {
+ for(i=0; i<e->ncont; i++) {
+ if (IsContElem(e,i)) printf("%s\n", ContElem(e,i)->gi);
+ else if (IsContData(e,i)) printf("#data %s\n", ContData(e,i));
+ else if (IsContPI(e,i)) printf("#pi %s\n", ContData(e,i));
+ }
+ return;
+ }
+
+ printf("Element: %s\tLineNumber: %d\n", e->gi, e->lineno);
+ if (e->parent)
+ printf("Context: %s\n", FindContext(e, 20, buf));
+
+ if (e->natts) {
+ printf("%d attributes:\n", e->natts);
+ for (i=0; i<e->natts; i++)
+ printf("\t%2d: %s = '%s'\n", i, e->atts[i].name, e->atts[i].sval);
+ }
+ if (e->entity) {
+ printf("Entity & notation information:\n");
+ if (e->entity->ename)
+ printf("Entity name: %s\n", e->entity->ename);
+ if (e->entity->nname)
+ printf("Notation name: %s\n", e->entity->nname);
+ if (e->entity->sysid)
+ printf("Sys id: %s\n", e->entity->sysid);
+ if (e->entity->pubid)
+ printf("Pub id: %s\n", e->entity->pubid);
+ if (e->entity->fname)
+ printf("Filename: %s\n", e->entity->fname);
+ }
+
+ if (e->my_eorder >= 0)
+ printf("My order among my siblings: %d\n", e->my_eorder);
+
+ if (e->necont) {
+ printf("%d child element nodes:\n", e->necont);
+ for(i=0; i<e->necont; i++) printf("\t%2d: %s\n", i, e->econt[i]->gi);
+ }
+
+ if (e->ndcont) {
+ printf("%d child data nodes:\n", e->ndcont);
+ for(i=0; i<e->ndcont; i++) {
+ if (strlen(e->dcont[i]) < 40)
+ printf("\t%2d: %s\n", i, e->dcont[i]);
+ else
+ printf("\t%2d: %-40.40s...\n", i, e->dcont[i]);
+ }
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Perform query. Syntax: find relationship gi. Tells whether gi has
+ * given relationship to current element. Result (message) sent to stdout.
+ * Args:
+ * Pointer to element under consideration.
+ * Pointer to name of relationship. (see FindRelByName() for names)
+ * Pointer to GI to look for.
+ */
+
+static void
+do_query(
+ Element_t *e,
+ char *rel,
+ char *gi
+)
+{
+ char *cp;
+ Relation_t r;
+ Element_t *ep;
+
+ for (cp=gi; *cp; cp++) if (islower(*cp)) *cp = toupper(*cp);
+
+ if ((r = FindRelByName(rel)) == REL_Unknown) {
+ return;
+ }
+ ep = QRelation(e, gi, r);
+ printf("%s, '%s' is%s %s of '%s'.\n", (ep ? "Yes" : "No"), gi,
+ (ep ? "" : " not"), rel, e->gi);
+}
+
+/* ______________________________________________________________________ */
+/* Print path to the element and its ID (if it has one) on a single line.
+ * Arguments:
+ * Pointer to element under consideration.
+ */
+static void
+PrElemPlusID(
+ Element_t *e
+)
+{
+ char buf[LINESIZE];
+
+ if (e->id) printf("%s -- ID=%s\n", FindElementPath(e, buf), e->id);
+ else printf("%s\n", FindElementPath(e, buf));
+}
+
+/* ______________________________________________________________________ */
+/* Print path to the element and its ID (if it has one) on a single line.
+ * Arguments:
+ * Pointer to element under consideration.
+ */
+
+static void
+match_gi(
+ Element_t *e,
+ char **av
+)
+{
+ if (!strcmp(av[1], e->gi)) PrElemPlusID(e);
+}
+
+/* Shorthand for defining simple finctions, which are just interfaces to
+ * calling QRelation(). DescendTree() only passes ptr to element. */
+#define MATCH(Fun,Rel) \
+ static void Fun(Element_t *e, char **av) \
+ { if (QRelation(e, av[1], Rel)) PrElemPlusID(e); }
+
+MATCH(match_parent, REL_Parent)
+MATCH(match_child, REL_Child)
+MATCH(match_anc, REL_Ancestor)
+MATCH(match_desc, REL_Descendant)
+MATCH(match_sib, REL_Sibling)
+
+static void
+match_attr(
+ Element_t *e,
+ char **av
+)
+{
+ char *atval;
+
+ if ((atval = FindAttValByName(e, av[1])) && !strcmp(av[2], atval))
+ PrElemPlusID(e);
+}
+
+static void
+match_cont(
+ Element_t *e,
+ char **av
+)
+{
+ int i;
+ for (i=0; i<e->ncont; i++) {
+ if (IsContData(e,i) && strstr(ContData(e,i), av[1])) {
+ PrElemPlusID(e);
+ return;
+ }
+ }
+}
+
+/* Find an element, given the criteria on its command line.
+ * Arguments:
+ * Pointer to element under consideration.
+ */
+static void
+do_find(
+ Element_t *e,
+ char **av
+)
+{
+ av++;
+ if (!strcmp(av[0], ".")) av++;
+ else e = DocTree;
+ if (!strcmp(av[0], "gi")) DescendTree(e, match_gi, 0, 0, av);
+ else if (!strcmp(av[0], "attr")) DescendTree(e, match_attr, 0, 0, av);
+ else if (!strcmp(av[0], "parent")) DescendTree(e, match_parent, 0, 0, av);
+ else if (!strcmp(av[0], "child")) DescendTree(e, match_child, 0, 0, av);
+ else if (!strcmp(av[0], "cont")) DescendTree(e, match_cont, 0, 0, av);
+ else if (!strcmp(av[0], "sib")) DescendTree(e, match_sib, 0, 0, av);
+ else if (!strcmp(av[0], "desc")) DescendTree(e, match_desc, 0, 0, av);
+ else if (!strcmp(av[0], "anc")) DescendTree(e, match_anc, 0, 0, av);
+ else fprintf(stderr, "Unknown find command: %s.\n", av[0]);
+}
+
+/* ______________________________________________________________________ */
diff --git a/usr.bin/sgmls/instant/general.h b/usr.bin/sgmls/instant/general.h
new file mode 100644
index 0000000..f6e6ea0
--- /dev/null
+++ b/usr.bin/sgmls/instant/general.h
@@ -0,0 +1,329 @@
+/*
+ * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 1994
+ * Open Software Foundation, Inc.
+ *
+ * Permission is hereby granted to use, copy, modify and freely distribute
+ * the software in this file and its documentation for any purpose without
+ * fee, provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation. Further, provided that the name of Open
+ * Software Foundation, Inc. ("OSF") not be used in advertising or
+ * publicity pertaining to distribution of the software without prior
+ * written permission from OSF. OSF makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Copyright (c) 1996 X Consortium
+ * Copyright (c) 1995, 1996 Dalrymple Consulting
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the X Consortium and
+ * Dalrymple Consulting shall not be used in advertising or otherwise to
+ * promote the sale, use or other dealings in this Software without prior
+ * written authorization.
+ */
+/* ________________________________________________________________________
+ *
+ * Common definitions for "instant" program.
+ * ________________________________________________________________________
+ */
+
+#ifdef STORAGE
+#ifndef lint
+static char *gen_h_RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/general.h,v 1.5 1996/06/11 20:25:03 fld Exp $";
+#endif
+#endif
+
+/* string/numeric/character definitions */
+
+#define EOS '\0'
+#define NL '\n'
+#define TAB '\t'
+#define CR '\r'
+#define ANCHOR '^'
+
+/* bigmask/flags for the Split() function */
+#define S_STRDUP 0x01
+#define S_ALVEC 0x02
+
+/* Command codes (1st char of esis lines) from sgmls. See its manpage. */
+#define CMD_DATA '-'
+#define CMD_OPEN '('
+#define CMD_CLOSE ')'
+#define CMD_ATT 'A'
+#define CMD_D_ATT 'D'
+#define CMD_NOTATION 'N'
+#define CMD_EXT_ENT 'E'
+#define CMD_INT_ENT 'I'
+#define CMD_SYSID 's'
+#define CMD_PUBID 'p'
+#define CMD_FILENAME 'f'
+#define CMD_LINE 'L'
+#define CMD_PI '?'
+#define CMD_SUBDOC 'S'
+#define CMD_SUBDOC_S '{'
+#define CMD_SUBDOC_E '}'
+#define CMD_EXT_REF '&'
+#define CMD_APPINFO '#'
+#define CMD_CONFORM 'C'
+
+/* Some sizes */
+#define MAX_DEPTH 40
+#define LINESIZE 60000
+
+/* Name of library env variable, and default value. */
+#ifndef TPT_LIB
+#define TPT_LIB "TPT_LIB"
+#endif
+#ifndef DEF_TPT_LIB
+#define DEF_TPT_LIB "/usr/share/sgml/transpec"
+#endif
+
+/* Relationships - for querying */
+typedef enum {
+ REL_None, REL_Parent, REL_Child, REL_Ancestor, REL_Descendant,
+ REL_Sibling, REL_Preceding, REL_ImmPreceding, REL_Following,
+ REL_ImmFollowing, REL_Cousin, REL_Unknown
+} Relation_t;
+
+/* Initial map sizes (IMS) */
+#define IMS_relations 3
+#define IMS_setvar 3
+#define IMS_incvar 3
+#define IMS_sdata 50
+#define IMS_sdatacache 30
+#define IMS_variables 20
+#define IMS_attnames 50
+#define IMS_elemnames 50
+
+/* ----- typedef and other misc definitions ----- */
+
+#ifndef TRUE
+#define TRUE (1 == 1)
+#endif
+
+#ifndef FALSE
+#define FALSE (1 == 0)
+#endif
+
+typedef short bool;
+
+
+/* ----- structure definitions ----- */
+
+/* We use this for variables, attributes, etc., so the caller only needs an
+ * opaque handle to the thing below, not worrying about array management. */
+typedef struct {
+ char *name; /* name of the thing */
+ char *sval; /* string value */
+} Mapping_t;
+
+typedef struct {
+ int n_alloc; /* number of elements allocated */
+ int n_used; /* number of elements used */
+ int slot_incr; /* increment for allocating slots */
+ int flags; /* info about this set of mappings */
+ Mapping_t *maps; /* array of mappings */
+} Map_t;
+
+/* ______________________________________________________________________ */
+
+/* Information about an entity reference. Not all fields will be used
+ * at once. */
+typedef struct _ent {
+ char *type; /* entity type */
+ char *ename; /* entity name */
+ char *nname; /* notation name */
+ char *sysid; /* sys id */
+ char *pubid; /* pub id */
+ char *fname; /* filename */
+ struct _ent *next; /* next in linked list */
+} Entity_t;
+
+/* Content (child nodes) of an element (node in the tree) -- both data
+ * and other elements. */
+typedef struct {
+ char type; /* element, data, or pi? */
+ union {
+ struct _elem *elem; /* direct children of this elem */
+ char *data; /* character data of this elem */
+ } ch;
+} Content_t;
+
+/* An element (node in the tree) */
+typedef struct _elem {
+ char *gi; /* element GI */
+ Content_t *cont; /* content - element & data children */
+ int ncont; /* # of content/children */
+ struct _elem **econt; /* element children */
+ int necont; /* # of element children */
+ char **dcont; /* character data children */
+ int ndcont; /* # of data children */
+ Mapping_t *atts; /* array of attributes */
+ int natts; /* # of attributes */
+ Entity_t *entity; /* ext entity & notation info */
+ char *id; /* for linking */
+ int index; /* an internal bookkeeping mechanism */
+ int depth; /* how deep in tree */
+ int lineno; /* line number */
+ char *infile; /* input filename */
+ int my_eorder; /* order of this elem of its parent */
+ struct _elem *parent; /* this elem's direct parent */
+ struct _elem *next; /* kept in linked list */
+ void *trans; /* pointer to translation spec */
+ /* I'm not crazy about this, but it works */
+ int gen_trans[2]; /* refs to generated trans specs */
+ int processed; /* was this node processed? */
+} Element_t;
+
+/* For mapping of element IDs to elements themselves. */
+typedef struct id_s {
+ char *id; /* ID of the element */
+ Element_t *elem; /* pointer to it */
+ struct id_s *next;
+} ID_t;
+
+/* ----- global variable declarations ----- */
+
+#ifdef STORAGE
+# define def
+#else
+# define def extern
+#endif
+
+def Element_t *DocTree; /* root of document tree */
+def char **UsedElem; /* a unique list of used elem names */
+def int nUsedElem; /* number of used elem names */
+def char **UsedAtt; /* a unique list of used attrib names */
+def int nUsedAtt; /* number of used attrib names */
+def ID_t *IDList; /* list of IDs used in the doc */
+def Map_t *Variables; /* general, global variables */
+def Map_t *SDATAmap; /* SDATA mappings */
+def Map_t *PImap; /* Processing Instruction mappings */
+def Entity_t *Entities; /* list of entities */
+
+def FILE *outfp; /* where output is written */
+def char *tpt_lib; /* TPT library directory */
+def int verbose; /* flag - verbose output? */
+def int warnings; /* flag - show warnings? */
+def int interactive; /* flag - interactive browsing? */
+def int slave; /* are we slave to another process? */
+def int fold_case; /* flag - fold case of GIs? */
+
+/* ----- some macros for convenience and ease of code reading ----- */
+
+#define stripNL(s) { char *_cp; if ((_cp=strchr(s, NL))) *_cp = EOS; }
+
+/* Similar to calloc(), malloc(), and realloc(), but check for success.
+ * Args to all:
+ * (1) number of 'elements' to allocate
+ * (2) variable to point at allocated space
+ * (3) type of 'element'
+ * Eg: Calloc(5, e, Element_t) replaces
+ * if (!(e = (Element_t *)calloc(5, sizeof(Element_t))) {
+ * ... handle error ... ;
+ * }
+ */
+#define Calloc(N,V,T) \
+ { if (!((V) = (T *)calloc((size_t)N, sizeof(T)))) { \
+ perror("Calloc failed -- out of memory. Bailing out."); exit(1); \
+ }; memset((void *) (V), 0, (size_t) sizeof(T)); }
+#define Malloc(N,V,T) \
+ { if (!((V) = (T *)malloc((size_t)N*sizeof(T)))) { \
+ perror("Malloc failed -- out of memory. Bailing out."); exit(1); \
+ }; memset((void *) (V), 0, (size_t) sizeof(T)); }
+#define Realloc(N,V,T) \
+ { if (!((V) = (T *)realloc(V,(size_t)N*sizeof(T)))) { \
+ perror("Realloc failed -- out of memory. Bailing out."); exit(1); \
+ } }
+
+/* similar to strcmp(), but check first chars first, for efficiency */
+#define StrEq(s1,s2) (s1[0] == s2[0] && !strcmp(s1,s2))
+
+/* similar to isspace(), but check for blank or tab - without overhead
+ * of procedure call */
+#define IsWhite(c) (c == ' ' || c == TAB || c == NL)
+
+#define ContType(e,i) (e->cont[i].type)
+#define ContData(e,i) (e->cont[i].ch.data)
+#define ContElem(e,i) (e->cont[i].ch.elem)
+#define IsContData(e,i) (e->cont[i].type == CMD_DATA)
+#define IsContElem(e,i) (e->cont[i].type == CMD_OPEN)
+#define IsContPI(e,i) (e->cont[i].type == CMD_PI)
+
+/* ----- function prototypes ----- */
+
+/* things defined in util.c */
+Element_t *QRelation(Element_t *, char *, Relation_t);
+Relation_t FindRelByName(char *);
+char *FindAttValByName(Element_t *, char *);
+char *FindContext(Element_t *, int, char *);
+char *AddElemName(char *);
+char *AddAttName(char *);
+char *ExpandString(char *);
+void OutputString(char *, FILE *, int);
+char *LookupSDATA(char *);
+FILE *OpenFile(char *);
+char *FilePath(char *);
+char *FindElementPath(Element_t *, char *);
+char *NearestOlderElem(Element_t *, char *);
+void PrintLocation(Element_t *, FILE *);
+char **Split(char *, int *, int);
+void DescendTree(Element_t *, void(*)(), void(*)(), void(*)(), void *);
+Map_t *NewMap(int);
+Mapping_t *FindMapping(Map_t *, const char *);
+char *FindMappingVal(Map_t *, const char *);
+void SetMapping(Map_t *, const char *);
+void SetMappingNV(Map_t *, const char *, const char *);
+void AddID(Element_t *, char *);
+Element_t *FindElemByID(char *);
+
+/* things defined in translate.c */
+void DoTranslate(Element_t*, FILE *);
+void ExpandVariables(char*, char*, Element_t*);
+
+/* things defined in traninit.c */
+void ReadTransSpec(char *);
+
+/* things defined in tranvar.c */
+char *Get_A_C_value(const char *);
+
+/* things defined in info.c */
+void PrintContext(Element_t *e);
+void PrintElemSummary(Element_t *);
+void PrintElemTree(Element_t *);
+void PrintStats(Element_t *);
+void PrintIDList();
+
+/* things defined in table.c */
+void CALStable(Element_t *, FILE *, char **, int);
+
+/* ----- other declarations ----- */
+
+#ifdef ultrix
+#define strdup(s) strcpy((char *)malloc(strlen(s)+1), s)
+#endif
+
diff --git a/usr.bin/sgmls/instant/hyper.c b/usr.bin/sgmls/instant/hyper.c
new file mode 100644
index 0000000..4f50b97
--- /dev/null
+++ b/usr.bin/sgmls/instant/hyper.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 1994
+ * Open Software Foundation, Inc.
+ *
+ * Permission is hereby granted to use, copy, modify and freely distribute
+ * the software in this file and its documentation for any purpose without
+ * fee, provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation. Further, provided that the name of Open
+ * Software Foundation, Inc. ("OSF") not be used in advertising or
+ * publicity pertaining to distribution of the software without prior
+ * written permission from OSF. OSF makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Copyright (c) 1996 X Consortium
+ * Copyright (c) 1995, 1996 Dalrymple Consulting
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the X Consortium and
+ * Dalrymple Consulting shall not be used in advertising or otherwise to
+ * promote the sale, use or other dealings in this Software without prior
+ * written authorization.
+ */
+/* ________________________________________________________________________
+ *
+ * Hypermedia-related facilities.
+ *
+ * Entry points for this module:
+ * AddID(elem, idval) add elem-id pair to list of known ids
+ * FindElemByID(idval) find elem by id
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/hyper.c,v 1.2 1996/06/02 21:46:10 fld Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/types.h>
+
+#include "general.h"
+
+
+/* ______________________________________________________________________ */
+
+void
+AddID(Element *e, char *idval)
+{
+ static ID *id_last;
+ if (!IDList) {
+ Calloc(1, id_last, ID);
+ IDList = id_last;
+ }
+ else {
+ Calloc(1, id_last->next, ID);
+ id_last = id_last->next;
+ }
+ id_last->elem = e;
+ id_last->id = idval;
+}
+
+Element *
+FindElemByID(char *idval)
+{
+ ID *id;
+ for (id=IDList; id; id=id->next)
+ if (!strcmp(id->id, idval)) return id->elem;
+ return 0;
+}
+
+/* ______________________________________________________________________ */
+
diff --git a/usr.bin/sgmls/instant/info.c b/usr.bin/sgmls/instant/info.c
new file mode 100644
index 0000000..27ab1c7
--- /dev/null
+++ b/usr.bin/sgmls/instant/info.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 1994
+ * Open Software Foundation, Inc.
+ *
+ * Permission is hereby granted to use, copy, modify and freely distribute
+ * the software in this file and its documentation for any purpose without
+ * fee, provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation. Further, provided that the name of Open
+ * Software Foundation, Inc. ("OSF") not be used in advertising or
+ * publicity pertaining to distribution of the software without prior
+ * written permission from OSF. OSF makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Copyright (c) 1996 X Consortium
+ * Copyright (c) 1995, 1996 Dalrymple Consulting
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the X Consortium and
+ * Dalrymple Consulting shall not be used in advertising or otherwise to
+ * promote the sale, use or other dealings in this Software without prior
+ * written authorization.
+ */
+/* ________________________________________________________________________
+ *
+ * Functions for printing information about an instance in the 'instant'
+ * program. Most of these are fairly short and simple.
+ *
+ * Entry points for this module:
+ * PrintElemSummary(elem) print summary info of each element
+ * PrintContext(elem) print context of each element
+ * PrintElemTree(elem) print tree of document
+ * PrintStats(elem) print statistics about doc tree
+ * PrintIDList(elem) print list of IDs and element context
+ * Most Print*() functions start at subtree pointed to by 'elem'.
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/info.c,v 1.2 1996/06/02 21:46:10 fld Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "general.h"
+
+/* ______________________________________________________________________ */
+/* Print a summary of each tag use in the instance. Things like depth in
+ * the tree, number of children, parent, attributes.
+ */
+
+/* Do the actual printing. Print the info about the node. If null,
+ * print a header for the columns.
+ * Arguments:
+ * Pointer to element structure of the node to print.
+ */
+static void
+print_summ(
+ Element_t *e
+)
+{
+ int i, n, dsize;
+ char *hfmt="%-18.18s %4s %5s %4s %4s %s\n";
+ char *fmt ="%-18.18s %4d %5d %4d %4d %s\n";
+
+ if (e == NULL) {
+ fprintf(outfp, hfmt, "Element", "Att", "Data", "Chd", "Dep", "Parent");
+ return;
+ }
+ for (i=0,n=0; i<e->ncont; i++) if (IsContElem(e,i)) n++;
+ for (i=0,dsize=0; i<e->ncont; i++)
+ if (IsContElem(e,i)) dsize += strlen(e->cont[i].ch.data);
+ fprintf(outfp, fmt, e->gi, e->natts, dsize, n, e->depth,
+ e->parent ? e->parent->gi : "-");
+
+ for (i=0; i<e->natts; i++) {
+ fprintf(outfp, "%45d: %s = %s\n", i, e->atts[i].name,
+ e->atts[i].sval ? e->atts[i].sval : "empty");
+ }
+}
+
+/* Descend the tree, calling processing routine.
+ * Arguments:
+ * Pointer to element structure at top of tree to traverse.
+ */
+void
+PrintElemSummary(
+ Element_t *e
+)
+{
+ print_summ(0);
+ DescendTree(e, print_summ, 0, 0, 0);
+}
+
+/* ______________________________________________________________________ */
+/* Print the context of each tag in the instance (i.e. the tag with its
+ * ancestors).
+ */
+
+/* Do the actual printing. Print the context of the node.
+ * Arguments:
+ * Pointer to element structure of the node to print.
+ */
+static void
+print_context(
+ Element_t *e
+)
+{
+ char buf[LINESIZE];
+
+ fprintf(outfp, "%-22s %s\n", e->gi, FindContext(e, 10, buf));
+}
+
+/* Descend the tree, calling processing routine.
+ * Arguments:
+ * Pointer to element structure at top of tree to traverse.
+ */
+void
+PrintContext(
+ Element_t *e
+)
+{
+ fprintf(outfp, "%-22s %s\n", "Element", "Context");
+ fprintf(outfp, "%-22s %s\n", "---------------", "-----------");
+ DescendTree(e, print_context, 0, 0, 0);
+
+ putc(NL, outfp);
+}
+
+/* ______________________________________________________________________ */
+/* Print tree of the instance. GI's are printed indented by their depth
+ * in the tree.
+ */
+
+/* Do the actual printing. Print the element name, indented the right amount.
+ * Arguments:
+ * Pointer to element structure of the node to print.
+ */
+static void
+print_indent(
+ Element_t *e
+)
+{
+ int i, ne, nd;
+ for(i=0; i<e->depth; i++) fputs(". ", outfp);
+ for(i=0,ne=0; i<e->ncont; i++) if (IsContElem(e,i)) ne++;
+ for(i=0,nd=0; i<e->ncont; i++) if IsContData(e,i) nd++;
+ fprintf(outfp, "%s (%d,%d)\n", e->gi, ne, nd);
+}
+
+/* Descend the tree, calling processing routine.
+ * Arguments:
+ * Pointer to element structure at top of tree to traverse.
+ */
+void
+PrintElemTree(
+ Element_t *e
+)
+{
+ DescendTree(e, print_indent, 0, 0, 0);
+ putc(NL, outfp);
+}
+
+/* ______________________________________________________________________ */
+/* Print some statistics about the instance.
+ */
+
+/* Accumulate the totals for the statistics.
+ * Arguments:
+ * Pointer to element structure of the node to print.
+ * Pointer to the total number of elements.
+ * Pointer to the total amount of content data.
+ * Pointer to the maximum depth of tree.
+ */
+static void
+acc_tots(
+ Element_t *e,
+ int *tot_el,
+ int *tot_data,
+ int *max_depth
+)
+{
+ int i;
+ for(i=0; i<e->necont; i++)
+ acc_tots(e->econt[i], tot_el, tot_data, max_depth);
+ for (i=0; i<e->necont; i++) (*tot_el)++;
+ for (i=0; i<e->ndcont; i++) (*tot_data) += strlen(e->dcont[i]);
+ if (e->depth > (*max_depth)) *max_depth = e->depth;
+}
+
+/* Descend the tree (recursively), collecting the statistics.
+ * Arguments:
+ * Pointer to element structure of the node to print.
+ * Pointer to the total number of elements.
+ * Pointer to the total amount of content data.
+ * Pointer to the maximum depth of tree.
+ */
+static void
+elem_usage(
+ Element_t *e,
+ char *name,
+ int *n_used,
+ int *nchars
+)
+{
+ int i;
+ if (!strcmp(name, e->gi)) {
+ (*n_used)++;
+ for (i=0; i<e->ncont; i++)
+ if (IsContData(e,i)) (*nchars) += strlen(ContData(e,i));
+ }
+ for(i=0; i<e->necont; i++)
+ elem_usage(e->econt[i], name, n_used, nchars);
+}
+
+/* Descend the tree, calling processing routine.
+ * Arguments:
+ * Pointer to element structure at top of tree to traverse.
+ */
+void
+PrintStats(
+ Element_t *top
+)
+{
+ int i, n;
+ int dif_el=0, tot_el=0, tot_data=0, nchars, max_depth=0;
+ float pct;
+
+ fprintf(outfp, "%-22s %s %s\n", "Element name", "Occurrances", "Character Content");
+ fprintf(outfp, "%-22s %s %s\n", "---------------", "-----------", "-----------------");
+
+ acc_tots(top, &tot_el, &tot_data, &max_depth);
+
+ for (i=0; i<nUsedElem; i++) {
+ n = 0;
+ nchars = 0;
+ elem_usage(top, UsedElem[i], &n, &nchars);
+ if (n > 0) {
+ pct = 100.0 * (float)n / (float)tot_el;
+ fprintf(outfp, "%-22s %4d %4.1f%% %6d %4d\n", UsedElem[i],
+ n, pct, nchars, (nchars/n));
+ dif_el++;
+ }
+ }
+
+ fprintf(outfp, "\nTotal of %d elements used, %d different ones.\n",
+ tot_el, dif_el);
+ fprintf(outfp, "Total character data: %d.\n", tot_data);
+ fprintf(outfp, "Maximum element depth: %d.\n", max_depth);
+ putc(NL, outfp);
+}
+
+/* ______________________________________________________________________ */
+/* Print list of: ID, GI, input file, line number, separated by colons.
+ * This is better for other programs to manipulate (like for keeping a
+ * database of IDs in documents) than humans to read.
+ */
+
+void
+PrintIDList()
+{
+ ID_t *id;
+ Element_t *ep;
+
+ for (id=IDList; id; id=id->next) {
+ ep = id->elem;
+ fprintf(outfp, "%s:%s:%s:%d\n", id->id, ep->gi,
+ ep->infile?ep->infile:"-", ep->lineno);
+ }
+}
+
+/* ______________________________________________________________________ */
+
diff --git a/usr.bin/sgmls/instant/instant.1 b/usr.bin/sgmls/instant/instant.1
new file mode 100644
index 0000000..513c84d
--- /dev/null
+++ b/usr.bin/sgmls/instant/instant.1
@@ -0,0 +1,183 @@
+...\"
+...\" Copyright (c) 1994
+...\" Open Software Foundation, Inc.
+...\"
+...\" Permission is hereby granted to use, copy, modify and freely distribute
+...\" the software in this file and its documentation for any purpose without
+...\" fee, provided that the above copyright notice appears in all copies and
+...\" that both the copyright notice and this permission notice appear in
+...\" supporting documentation. Further, provided that the name of Open
+...\" Software Foundation, Inc. ("OSF") not be used in advertising or
+...\" publicity pertaining to distribution of the software without prior
+...\" written permission from OSF. OSF makes no representations about the
+...\" suitability of this software for any purpose. It is provided "as is"
+...\" without express or implied warranty.
+...\"
+...\" Copyright (c) 1996 X Consortium
+...\" Copyright (c) 1996 Dalrymple Consulting
+...\"
+...\" Permission is hereby granted, free of charge, to any person obtaining a copy
+...\" of this software and associated documentation files (the "Software"), to deal
+...\" in the Software without restriction, including without limitation the rights
+...\" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+...\" copies of the Software, and to permit persons to whom the Software is
+...\" furnished to do so, subject to the following conditions:
+...\"
+...\" The above copyright notice and this permission notice shall be included in
+...\" all copies or substantial portions of the Software.
+...\"
+...\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+...\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+...\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+...\" X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+...\" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+...\" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+...\" OTHER DEALINGS IN THE SOFTWARE.
+...\"
+...\" Except as contained in this notice, the names of the X Consortium and
+...\" Dalrymple Consulting shall not be used in advertising or otherwise to
+...\" promote the sale, use or other dealings in this Software without prior
+...\" written authorization.
+...\"
+...\" Translated with /usr/local/lib/tpt/ref-man.ts by fld on cord, Wed 07 Feb 1996, 21:59
+.Dd September 5, 1996
+.Os FreeBSD 2.2
+.Dt SGMLFMT 1
+.Sh "NAME"
+instant - manipulates ESIS from parsed SGML instance
+.Sh "Synopsis"
+.na
+.Pp
+\fBinstant\fP [ \fB-bhuvxHISW\fP ] [ \fB-t\fP \fIfile\fP] [ \fB-o\fP \fIfile\fP] [ \fB-D\fP \fIvariable\fP\fB=\fP\fIvalue\fP ...] [ \fB-i\fP \fIid\fP] [ \fB-l\fP \fIdirectory\fP] [\fIfile\fP]
+.ad
+.Sh "DESCRIPTION"
+.Pp
+The \fBinstant\fP program manipulates an SGML document instance in a variety of ways,
+including translating into a form suitable for a formatting application and printing information about this instance.
+Input to \fBinstant\fP is the output of \fBsgmls\fP, whose format is called Element Structure Information Set (ESIS).
+.Sh "FLAGS"
+.Pp
+The following are the possible command line options to \fBinstant\fP. Output is sent to the standard output, except where otherwise noted.
+.\"'br\" labeled list
+.Bl -tag -width Ds
+.It "\fB-t\fP \fIfile\fP"
+Translate the SGML instance to another form, usually suitable for a formatting application.
+The \fIfile\fP is called a translation spec, which specifies how the tags are to be translated. See \fBtranspec\fP(4).
+By convention, names for \fIfile\fP use the suffix \fB.ts\fP, for \fItranslation spec\fP.
+.It "\fB-d\fP"
+"Data hack" \(em strip newline at the beginning of data records
+.It "\fB-f \fIlength\fR"
+Set the threshold for the length, in characters,
+of an <Entry>, over which it is called a block of filled text, to \fIlength\fR.
+.It "\fB-o\fP \fIfile\fP "
+Write all output (except error and warning messages) to file \fIfile\fP. By default, output goes to stdout.
+.It "\fB-h\fP"
+Print a text representation of the hierarchy of the instance elements.
+The deeper an element is in the tree, the more it is indented. The data content is not printed.
+.It "\fB-u\fP"
+Print a summary of the usage of each element in the instance.
+Information given includes attributes, number of children, and depth in the hierarchy.
+.It "\fB-S\fP"
+Print some statistics about element usage in the instance, including how often each element is used
+and how much PCDATA is contained.
+.It "\fB-x\fP"
+Print the context of each element in the instance, from each element to the root of the document tree.
+.It "\fB-v\fP"
+Validate the SGML instance based on the set of constraints or descriptions in the transpec file.
+This flags tells \fBinstant\fP to turn off normal output, leaving only diagnostics.
+.It "\fB-l\fP \fIdirectory\fP"
+Try to read the translation specs or other files from in the directory \fIdirectory\fP
+if not found in the current directory.
+This is called the library directory.
+The environment variable \fITPT_LIB\fP may also be used to specify this.
+.It "\fB-b\fP"
+Interactive browser mode. The user is prompted for actions,
+which include moving among and examining the various nodes in the hierarchy of the instance, displaying information about them, etc.
+.It "\fB-I\fP"
+List the IDs of all elements in the instance that have an ID. The format is more suitable for other programs than humans.
+Lines show the ID, element GI, filename, and line, separated by colons.
+(This depends on the \fB-l\fP option to \fBsgmls\fP which provide filenames and line numbers.)
+.It "\fB-i\fP \fIid\fP"
+When translating the instance, begin processing at the element whose ID is \fIid\fP instead of the topmost element.
+.It "\fB-D\fP \fIvariable\fP\fB=\fP\fIvalue\fP"
+Define the variable \fIvariable\fP with value \fIvalue\fP.
+.It "\fB-W\fP"
+Do not print warning messages.
+.It "\fB-H\fP"
+Print a help message briefly describing the options.
+.It "\fIfile\fP"
+Read the instance from the file \fIfile\fP.
+This is expected to be the output of the program \fBsgmls\fP.
+If not specified, \fBinstant\fP reads the instance from its standard input.
+.El
+.\"'br\" labeled list end
+.Pp
+In some cases it makes no sense to combine options.
+This is especially true if one of the options is to perform a translation. No checking is done for this.
+.Sh "INTERACTIVE BROWSER"
+.Pp
+These are the commands to the interactive browser:
+.Bl -tag -width Ds
+.\"'br\" labeled list
+.It "\fBcd\fP \fIargs ...\fP"
+Change to a different element in the hierarchy.
+\fBcd\fP \fBid\fP \fIid\fP will change to the element whose ID is \fIid\fP.
+\fBcd\fP \fIN\fP will change to the \fIN\fPth child element of the current element.
+Several values of \fIN\fP may be specified, so the program will change to successively descending elements in the hierarchy.
+The string \fB..\fP may appear for \fIN\fP to move up a level in the hierarchy (like in a unix file system).
+A \fB/\fP may be specified for \fIN\fP to change to the top of the hierarchy.
+.It "\fBcont\fP"
+Print the context of each element.
+.It "\fBdata\fP \fIN\fP"
+Show the data content (PCDATA, RCDATA, and DATA) of child node N.
+.It "\fBfind\fP \fIspec\fP"
+Find paths to elements matching \fIspec\fP, where \fIspec\fP may be one of:
+.Bl -tag -width Ds
+.\".RS +\n(INu
+.It "\fBparent\fP \fIgi\fP"
+Find all elements whose parent element is \fIgi\fP.
+.It "\fBchild\fP \fIgi\fP"
+Find all elements which have a child element \fIgi\fP.
+.It "\fBgi\fP \fIgi\fP"
+Find all elements whose name is \fIgi\fP.
+.It "\fBattr\fP \fIname\fP \fIvalue\fP"
+Find all elements that have a attribute \fIname\fP that have a value \fIvalue\fP.
+.\".RE
+.El
+.It "\fBid\fP \fIID\fP"
+Show location of element whose ID is \fIID\fP.
+If \fIID\fP is \fB?\fP, it will list all IDs with the paths to them.
+.It "\fBls\fP"
+List information about the current element in the hierarchy.
+This includes element name, line number in instance, context, attributes and their values, child elements, data directly within this element,
+and the order of the current element among its siblings.
+.It "\fBq\fP \fIrelation\fP \fIelement\fP"
+Report whether or not the current element has the relation \fIrelation\fP to the named element \fIelement\fP.
+Values of \fIrelation\fP are the same as for \fB_followrel\fP in \fBtranspec\fP reference page.
+.It "\fBstat\fP"
+Show statistics about the hierarchy.
+.It "\fBsum\fP"
+Show a tag usage summary about the hierarchy.
+.It "\fBtran\fP \fIoutfile\fP"
+Write translated output to file \fIoutfile\fP.
+If \fIoutfile\fP is not specified, output is sent to stdout.
+.It "\fBtree\fP"
+Print a textual representation of the hierarchy of the instance, where deeper elements are indented more.
+.It "\fBwhere\fP"
+Show current position in the hierarchy.
+.It "<\fBcontrol-D\fP>"
+Exits the program.
+.El
+.Pp
+The \fBstat\fP, \fBsum\fP, \fBtree\fP, \fBcont\fP commands take an optional first argument (of any value),
+which means to only consider the entire instance instead of the hierarchy from the current element.
+.Sh "FILES"
+.Bl -tag -width Ds
+.It "\fIfile\fP\fB.ts\fP"
+Translation specification file.
+.El
+.Sh "SEE ALSO"
+.Pp
+.Xr transpec 5 ,
+.Xr sgmls 1 ,
+Standard Generalized Markup Language (SGML), ISO 8879.
diff --git a/usr.bin/sgmls/instant/main.c b/usr.bin/sgmls/instant/main.c
new file mode 100644
index 0000000..2f10537
--- /dev/null
+++ b/usr.bin/sgmls/instant/main.c
@@ -0,0 +1,710 @@
+/*
+ * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 1994
+ * Open Software Foundation, Inc.
+ *
+ * Permission is hereby granted to use, copy, modify and freely distribute
+ * the software in this file and its documentation for any purpose without
+ * fee, provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation. Further, provided that the name of Open
+ * Software Foundation, Inc. ("OSF") not be used in advertising or
+ * publicity pertaining to distribution of the software without prior
+ * written permission from OSF. OSF makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Copyright (c) 1996 X Consortium
+ * Copyright (c) 1995, 1996 Dalrymple Consulting
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the X Consortium and
+ * Dalrymple Consulting shall not be used in advertising or otherwise to
+ * promote the sale, use or other dealings in this Software without prior
+ * written authorization.
+ */
+/* ________________________________________________________________________
+ *
+ * Program to read an SGML document instance, creating any of several things:
+ *
+ * "translated" output for formatting applications (given a trans. spec)
+ * validation report (given a appropriate trans spec)
+ * tree of the document's structure
+ * statistics about the element usage
+ * summary of the elements used
+ * context of each element used
+ * IDs of each element
+ *
+ * A C structure is created for each element, which includes:
+ * name, attributes, parent, children, content
+ * The tree is descended, and the desired actions performed.
+ *
+ * Takes input from James Clark's "sgmls" program (v. 1.1).
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /home/ncvs/src/usr.bin/sgmls/instant/main.c,v 1.1.1.1 1996/09/08 01:55:10 jfieber Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <memory.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <time.h>
+#include <locale.h>
+
+#define STORAGE
+#include "general.h"
+
+static int do_context, do_tree, do_summ, do_stats, do_validate, do_idlist;
+static int do_DATAhack = 0;
+static char *this_prog;
+static char *in_file, *out_file;
+static char *tranfile;
+static char *start_id;
+static char *last_file;
+static int last_lineno;
+
+extern int BOFTTextThresh;
+
+/* forward references */
+static void HandleArgs(int, char *[]);
+static void Initialize1();
+static void Initialize2();
+static void ReadInstance(char *);
+static void DoHelpMessage();
+extern void Browse();
+
+/* external reference to version number */
+char _HeadVeRsIoN_[] = "1.0 (FreeBSD)";
+
+/* ______________________________________________________________________ */
+/* Program entry point. Look at args, read instance, dispatch to the
+ * correct routines to do the work, and finish.
+ */
+
+int
+main(
+ int ac,
+ char *av[]
+)
+{
+ setlocale(LC_ALL, "");
+ Initialize1(av[0]);
+ HandleArgs(ac, av);
+ Initialize2();
+
+ if (tranfile) ReadTransSpec(tranfile);
+ ReadInstance(in_file);
+
+ if (interactive) {
+ Browse(); /* this will handle interactive commands */
+ }
+ else {
+ /* Perform tasks based on command line flags... */
+ if (tranfile) {
+ Element_t *e;
+ /* If user wants to start at a particular ID, point to that
+ * element. Else, point to the top of the tree. */
+ if (start_id) {
+ if (!(e=FindElemByID(start_id))) {
+ fprintf(stderr, "Error: Can not find element with ID %s\n",
+ start_id);
+ exit(1);
+ }
+ }
+ else e = DocTree;
+ /* If we're doing validation, make output file pointer null.
+ * This means that we generate no output, except error messages. */
+ if (do_validate) outfp = NULL;
+ if (tranfile)
+ DoTranslate(e, outfp);
+ else
+ fprintf(stderr, "Translation spec file not specified. Skipping translation.\n");
+ }
+ if (do_summ) PrintElemSummary(DocTree);
+ if (do_tree) PrintElemTree(DocTree);
+ if (do_stats) PrintStats(DocTree);
+ if (do_context) PrintContext(DocTree);
+ if (do_idlist) PrintIDList();
+ }
+ if (out_file && outfp) fclose(outfp);
+
+ return 0;
+}
+
+/* ______________________________________________________________________ */
+/* Initialization stuff done before dealing with args.
+ * Arguments:
+ * Name of program (string).
+ */
+
+static void
+Initialize1(
+ char *myname
+)
+{
+ time_t tnow;
+ struct tm *nowtm;
+ char *cp, buf[100];
+ extern int gethostname(char *, int); /* not in a system .h file... */
+
+ /* where we try to find data/library files */
+ if (!(tpt_lib=getenv(TPT_LIB))) tpt_lib = DEF_TPT_LIB;
+
+ /* set some global variables */
+ warnings = 1;
+ fold_case = 1;
+ this_prog = myname;
+
+ /* setup global variable mapping */
+ Variables = NewMap(IMS_variables);
+
+ /* set some pre-defined variables */
+ SetMappingNV(Variables, "user", (cp=getenv("USER")) ? cp : "UnknownUser" );
+ time(&tnow);
+ nowtm = localtime(&tnow);
+ strftime(buf, 100, "%a %d %b %Y, %R", nowtm);
+ SetMappingNV(Variables, "date", buf);
+ if (gethostname(buf, 100) < 0) strcpy(buf, "unknown-host");
+ SetMappingNV(Variables, "host", buf);
+ SetMappingNV(Variables, "transpec", tranfile ? tranfile : "??");
+}
+
+/* Initialization stuff done after dealing with args. */
+
+static void
+Initialize2()
+{
+ SetMappingNV(Variables, "transpec", tranfile ? tranfile : "??");
+
+ /* If user wants to send output to a file, open the file, and set
+ * the file pointer. Else we send output to standard out. */
+ if (out_file) {
+ if (!(outfp = fopen(out_file, "w"))) {
+ fprintf(stderr, "Could not open output '%s' file for writing.\n%s",
+ out_file, strerror(errno));
+ exit(1);
+ }
+ }
+ else outfp = stdout;
+}
+
+/* ______________________________________________________________________ */
+/* Set a variable. If it is one of the "known" variables, set the
+ * variable in the C code (this program).
+ * Arguments:
+ * Variable name/value string - separated by an '=' (eg, "myname=Sally").
+ */
+static void
+CmdLineSetVariable(
+ char *var
+)
+{
+ char *cp, buf[100], **tok;
+ int n;
+
+ /* Turn '=' into a space, to isolate the name. Then set variable. */
+ strcpy(buf, var);
+ if ((cp=strchr(buf, '='))) {
+ /* we have "var=value" */
+ *cp = ' ';
+ n = 2;
+ tok = Split(buf, &n, 0);
+ /* see if variable name matches one of our internal ones */
+ if (!strcmp(tok[0], "verbose")) verbose = atoi(tok[1]);
+ else if (!strcmp(tok[0], "warnings")) warnings = atoi(tok[1]);
+ else if (!strcmp(tok[0], "foldcase")) fold_case = atoi(tok[1]);
+ else SetMappingNV(Variables, tok[0], tok[1]);
+ }
+ else {
+ fprintf(stderr, "Expected an '=' in variable assignment: %s. Ignored\n",
+ var);
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Bounce through arguments, setting variables and flags.
+ * Arguments:
+ * Argc and Argv, as passed to main().
+ */
+static void
+HandleArgs(
+ int ac,
+ char *av[]
+)
+{
+ int c, errflag=0;
+ extern char *optarg;
+ extern int optind;
+
+ while ((c=getopt(ac, av, "df:t:v:o:huSxIl:bHVWi:D:Z")) != -1) {
+ switch (c) {
+ case 't': tranfile = optarg; break;
+ case 'v': do_validate = 1; break;
+ case 'h': do_tree = 1; break;
+ case 'u': do_summ = 1; break;
+ case 'S': do_stats = 1; break;
+ case 'x': do_context = 1; break;
+ case 'I': do_idlist = 1; break;
+ case 'l': tpt_lib = optarg; break;
+ case 'i': start_id = optarg; break;
+ case 'o': out_file = optarg; break;
+ case 'd': do_DATAhack = 1; break;
+ case 'f': BOFTTextThresh = atoi(optarg); break;
+ case 'b': interactive = 1; break;
+ case 'W': warnings = 0; break;
+ case 'V': verbose = 1; break;
+ case 'Z': slave = 1; break;
+ case 'H': DoHelpMessage(); exit(0); break;
+ case 'D': CmdLineSetVariable(optarg); break;
+ case '?': errflag = 1; break;
+ }
+ if (errflag) {
+ fprintf(stderr, "Try '%s -H' for help.\n", this_prog);
+ exit(1);
+ }
+ }
+
+ /* input (ESIS) file name */
+ if (optind < ac) in_file = av[optind];
+
+ /* If doing interactive/browsing, we can't take ESIS from stdin. */
+ if (interactive && !in_file) {
+ fprintf(stderr,
+ "You must specify ESIS file on cmd line for browser mode.\n");
+ exit(1);
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Simply print out a help/usage message.
+ */
+
+static char *help_msg[] = {
+ "",
+ " -t file Print translated output using translation spec in <file>",
+ " -v Validate using translation spec specified with -t",
+ " -i id Consider only subtree starting at element with ID <id>",
+ " -b Interactive browser",
+ " -S Print statistics (how often elements occur, etc.)",
+ " -u Print element usage summary (# of children, depth, etc.)",
+ " -x Print context of each element",
+ " -h Print document hierarchy as a tree",
+ " -o file Write output to <file>. Default is standard output.",
+ " -l dir Set library directory to <dir>. (or env. variable TPT_LIB)",
+ " -I List all IDs used in the instance",
+ " -W Do not print warning messages",
+ " -H Print this help message",
+ " -Dvar=val Set variable 'var' to value 'val'",
+ " file Take input from named file. If not specified, assume stdin.",
+ " File should be output from the 'sgmls' program (ESIS).",
+ NULL
+};
+
+static void
+DoHelpMessage()
+{
+ char **s = help_msg;
+ printf("usage: %s [option ...] [file]", this_prog);
+ while (*s) puts(*s++);
+ printf("\nVersion: %s\n", _HeadVeRsIoN_);
+}
+
+/* ______________________________________________________________________ */
+/* Remember an external entity for future reference.
+ * Arguments:
+ * Pointer to entity structure to remember.
+ */
+
+static void
+AddEntity(
+ Entity_t *ent
+)
+{
+ static Entity_t *last_ent;
+
+ if (!Entities) {
+ Malloc(1, Entities, Entity_t);
+ last_ent = Entities;
+ }
+ else {
+ Malloc(1, last_ent->next, Entity_t);
+ last_ent = last_ent->next;
+ }
+ *last_ent = *ent;
+
+}
+
+/* Find an entity, given its entity name.
+ * Arguments:
+ * Name of entity to retrieve.
+ */
+static Entity_t *
+FindEntity(
+ char *ename
+)
+{
+ Entity_t *n;
+ for (n=Entities; n; n=n->next)
+ if (StrEq(ename, n->ename)) return n;
+ return 0;
+}
+
+/* Accumulate lines up to the open tag. Attributes, line number,
+ * entity info, notation info, etc., all come before the open tag.
+ */
+static Element_t *
+AccumElemInfo(
+ FILE *fp
+)
+{
+ char buf[LINESIZE+1];
+ int c;
+ int i, na;
+ char *cp, *atval;
+ Mapping_t a[100];
+ Element_t *e;
+ Entity_t ent, *ent2;
+ char **tok;
+ static int Index=0;
+ static Element_t *last_e;
+
+
+ Calloc(1, e, Element_t);
+ memset(&ent, 0, sizeof ent); /* clean space for entity info */
+
+ /* Also, keep a linked list of elements, so we can easily scan through */
+ if (last_e) last_e->next = e;
+ last_e = e;
+
+ e->index = Index++; /* just a unique number for identification */
+
+ /* in case these are not set for this element in the ESIS */
+ e->lineno = last_lineno;
+ e->infile = last_file;
+
+ na = 0;
+ while (1) {
+ if ((c = getc(fp)) == EOF) break;
+ fgets(buf, LINESIZE, fp);
+ stripNL(buf);
+ switch (c) {
+ case EOF: /* End of input */
+ fprintf(stderr, "Error: Unexpectedly reached end of ESIS.\n");
+ exit(1);
+ break;
+
+ case CMD_OPEN: /* (gi */
+ e->gi = AddElemName(buf);
+ if (na > 0) {
+ Malloc(na, e->atts, Mapping_t);
+ memcpy(e->atts, a, na*sizeof(Mapping_t));
+ e->natts = na;
+ na = 0;
+ }
+ /* Check if this elem has a notation attr. If yes, and there
+ is no notation specified, recall the previous one. (feature
+ of sgmls - it does not repeat notation stuff if we the same
+ is used twice in a row) */
+ if (((atval=FindAttValByName(e, "NAME")) ||
+ (atval=FindAttValByName(e, "ENTITYREF")) ||
+ (atval=FindAttValByName(e, "EXTERNAL"))) && /* HACK */
+ (ent2=FindEntity(atval))) {
+ e->entity = ent2;
+ }
+
+ return e;
+ break;
+
+ case CMD_ATT: /* Aname val */
+ i = 3;
+ tok = Split(buf, &i, 0);
+ if (!strcmp(tok[1], "IMPLIED"))
+ break; /* skip IMPLIED atts. */
+ else if (!strcmp(tok[1], "CDATA"))
+ {
+ /* CDATA attributes must have ESIS escape
+ sequences and SDATA entities expanded. */
+ char *val = ExpandString(tok[2]);
+ a[na].name = AddAttName(tok[0]);
+ a[na].sval = AddAttName(val);
+ free(val);
+ na++;
+ }
+ else if (!strcmp(tok[1], "TOKEN") ||
+ !strcmp(tok[1], "ENTITY") ||!strcmp(tok[1], "NOTATION"))
+ {
+ a[na].name = AddAttName(tok[0]);
+ a[na].sval = AddAttName(tok[2]);
+ na++;
+ }
+ else {
+ fprintf(stderr, "Error: Bad attr line (%d): A%s %s...\n",
+ e->lineno, tok[0], tok[1]);
+ }
+ break;
+
+ case CMD_LINE: /* Llineno */
+ /* These lines come in 2 forms: "L123" and "L123 file.sgml".
+ * Filename is given only at 1st occurance. Remember it.
+ */
+ if ((cp = strchr(buf, ' '))) {
+ cp++;
+ last_file = strdup(cp);
+ }
+ last_lineno = e->lineno = atoi(buf);
+ e->infile = last_file;
+ break;
+
+ case CMD_DATA: /* -data */
+ fprintf(stderr, "Error: Data in AccumElemInfo, line %d:\n%c%s\n",
+ e->lineno, c,buf);
+ /*return e;*/
+ exit(1);
+ break;
+
+ case CMD_D_ATT: /* Dename name val */
+
+ case CMD_NOTATION: /* Nnname */
+ case CMD_PI: /* ?pi */
+ /* This should be reworked soon, as it
+ forces all PI's before the first GI
+ to be ignored. -CSS */
+ break;
+
+ case CMD_EXT_ENT: /* Eename typ nname */
+ i = 3;
+ tok = Split(buf, &i, 0);
+ ent.ename = strdup(tok[0]);
+ ent.type = strdup(tok[1]);
+ ent.nname = strdup(tok[2]);
+ AddEntity(&ent);
+ break;
+ case CMD_INT_ENT: /* Iename typ text */
+ fprintf(stderr, "Error: Got CMD_INT_ENT in AccumElemInfo: %s\n", buf);
+ break;
+ case CMD_SYSID: /* ssysid */
+ ent.sysid = strdup(buf);
+ break;
+ case CMD_PUBID: /* ppubid */
+ ent.pubid = strdup(buf);
+ break;
+ case CMD_FILENAME: /* ffilename */
+ ent.fname = strdup(buf);
+ break;
+
+ case CMD_CLOSE: /* )gi */
+ case CMD_SUBDOC: /* Sename */
+ case CMD_SUBDOC_S: /* {ename */
+ case CMD_SUBDOC_E: /* }ename */
+ case CMD_EXT_REF: /* &name */
+ case CMD_APPINFO: /* #text */
+ case CMD_CONFORM: /* C */
+ default:
+ fprintf(stderr, "Error: Unexpected input in AccumElemInfo, %d:\n%c%s\n",
+ e->lineno, c,buf);
+ exit(1);
+ break;
+ }
+ }
+ fprintf(stderr, "Error: End of AccumElemInfo - should not be here: %s\n",
+ e->gi);
+/* return e;*/
+ exit(1);
+}
+
+/* Read ESIS lines.
+ * Limitation? Max 5000 children per node. (done for efficiency --
+ * should do some malloc and bookkeeping games later).
+ */
+
+static Element_t *
+ReadESIS(
+ FILE *fp,
+ int depth
+)
+{
+ char *buf;
+ int i, c, ncont;
+ Element_t *e;
+ Content_t cont[5000];
+
+ Malloc( LINESIZE+1, buf, char );
+
+ /* Read input stream - the output of "sgmls", called "ESIS". */
+ e = AccumElemInfo(fp);
+ e->depth = depth;
+
+ ncont = 0;
+ while (1) {
+ if ((c = getc(fp)) == EOF) break;
+ switch (c) {
+ case EOF: /* End of input */
+ break;
+
+ case CMD_DATA: /* -data */
+ fgets(buf, LINESIZE, fp);
+ stripNL(buf);
+ if (do_DATAhack && (buf[0] == '\\') && (buf[1] == 'n')) {
+ if ( ! buf[2] )
+ break;
+ buf[0] = ' ';
+ memcpy(&buf[1], &buf[2], strlen(buf)-1);
+ }
+ cont[ncont].ch.data = ExpandString(buf);
+ cont[ncont].type = CMD_DATA;
+ ncont++;
+ break;
+
+ case CMD_PI: /* ?pi */
+ fgets(buf, LINESIZE, fp);
+ stripNL(buf);
+ cont[ncont].type = CMD_PI;
+ cont[ncont].ch.data = strdup(buf);
+ ncont++;
+ break;
+
+ case CMD_CLOSE: /* )gi */
+ fgets(buf, LINESIZE, fp);
+ stripNL(buf);
+ if (ncont) {
+ e->ncont = ncont;
+ Malloc(ncont, e->cont, Content_t);
+ for (i=0; i<ncont; i++) e->cont[i] = cont[i];
+ }
+ free(buf);
+ return e;
+ break;
+
+ case CMD_OPEN: /* (gi */
+/*fprintf(stderr, "+++++ OPEN +++\n");*/
+/* break;*/
+
+ case CMD_ATT: /* Aname val */
+ case CMD_D_ATT: /* Dename name val */
+ case CMD_NOTATION: /* Nnname */
+ case CMD_EXT_ENT: /* Eename typ nname */
+ case CMD_INT_ENT: /* Iename typ text */
+ case CMD_SYSID: /* ssysid */
+ case CMD_PUBID: /* ppubid */
+ case CMD_FILENAME: /* ffilename */
+ ungetc(c, fp);
+ cont[ncont].ch.elem = ReadESIS(fp, depth+1);
+ cont[ncont].type = CMD_OPEN;
+ cont[ncont].ch.elem->parent = e;
+ ncont++;
+ break;
+
+ case CMD_LINE: /* Llineno */
+ fgets(buf, LINESIZE, fp);
+ break; /* ignore these here */
+
+ case CMD_SUBDOC: /* Sename */
+ case CMD_SUBDOC_S: /* {ename */
+ case CMD_SUBDOC_E: /* }ename */
+ case CMD_EXT_REF: /* &name */
+ case CMD_APPINFO: /* #text */
+ case CMD_CONFORM: /* C */
+ default:
+ fgets(buf, LINESIZE, fp);
+ fprintf(stderr, "Error: Unexpected input at %d: '%c%s'\n",
+ e->lineno, c, buf);
+ exit(1);
+ break;
+ }
+ }
+ fprintf(stderr, "Error: End of ReadESIS - should not be here: %s\n", e->gi);
+ free(buf);
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* Read input stream, creating a tree in memory of the elements and data.
+ * Arguments:
+ * Filename where instance's ESIS is.
+ */
+static void
+ReadInstance(
+ char *filename
+)
+{
+ int i, n;
+ FILE *fp;
+ Element_t *e;
+ char *idatt;
+
+ if (filename) { /* if we specified input file. else stdin */
+ if ((fp=fopen(filename, "r")) == NULL) {
+ perror(filename);
+ exit(1);
+ }
+ }
+ else fp = stdin;
+ last_file = filename;
+ DocTree = ReadESIS(fp, 0);
+ if (filename) fclose(fp);
+
+ /* Traverse tree, filling in econt and figuring out which child
+ * (ie. what birth order) each element is. */
+ DocTree->my_eorder = -1;
+ for (e=DocTree; e; e=e->next) {
+
+ /* count element children */
+ for (i=0,n=0; i<e->ncont; i++) if (IsContElem(e,i)) n++;
+ if (n > 0) Calloc(n, e->econt, Element_t *);
+ for (i=0; i<e->ncont; i++)
+ if (IsContElem(e,i)) e->econt[e->necont++] = ContElem(e,i);
+
+ /* count data children */
+ for (i=0,n=0; i<e->ncont; i++) if (IsContData(e,i)) n++;
+ if (n > 0) Calloc(n, e->dcont, char *);
+ for (i=0; i<e->ncont; i++)
+ if (IsContData(e,i)) e->dcont[e->ndcont++] = ContData(e,i);
+
+ /* where in child order order */
+ for (i=0; i<e->necont; i++)
+ e->econt[i]->my_eorder = i;
+
+ /* Does this element have an ID? */
+ for (i=0; i<e->natts; i++) {
+ if ((idatt=FindAttValByName(e, "ID"))) {
+ AddID(e, idatt);
+ /* remember ID value for quick reference */
+ e->id = idatt;
+ break;
+ }
+ }
+ }
+ return;
+}
+
+/* ______________________________________________________________________ */
diff --git a/usr.bin/sgmls/instant/tables.c b/usr.bin/sgmls/instant/tables.c
new file mode 100644
index 0000000..54fd211
--- /dev/null
+++ b/usr.bin/sgmls/instant/tables.c
@@ -0,0 +1,2013 @@
+/*
+ * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 1994
+ * Open Software Foundation, Inc.
+ *
+ * Permission is hereby granted to use, copy, modify and freely distribute
+ * the software in this file and its documentation for any purpose without
+ * fee, provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation. Further, provided that the name of Open
+ * Software Foundation, Inc. ("OSF") not be used in advertising or
+ * publicity pertaining to distribution of the software without prior
+ * written permission from OSF. OSF makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Copyright (c) 1996 X Consortium
+ * Copyright (c) 1995, 1996 Dalrymple Consulting
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the X Consortium and
+ * Dalrymple Consulting shall not be used in advertising or otherwise to
+ * promote the sale, use or other dealings in this Software without prior
+ * written authorization.
+ */
+/* ________________________________________________________________________
+ *
+ * Program to manipulate SGML instances.
+ *
+ * Originally coded for OSF DTD tables, now recoded (fld 3/27/95)
+ * for CALS-type tables (fragment taken from the DocBook DTD). Then,
+ * *really* upgraded to CALS tables by FLD on 5/28/96.
+ *
+ * This module is for handling table markup, printing TeX or tbl
+ * (tbl) markup to the output stream. Also, table markup checking is
+ * done here. Yes, this depends on the DTD, but it makes translation
+ * specs much cleaner (and makes some things possible).
+ *
+ * Incomplete / not implemented / limitations / notes:
+ * vertical alignment (valign attr)
+ * vertical spanning
+ * row separators are for the whole line, not per cell (the prog looks
+ * at rowsep for the 1st cell and applies it to the whole row)
+ * trusts that units in colwidths are acceptable to LaTeX and tbl
+ * "s" is an acceptable shorthand for "span" in model attributes
+ *
+ * A note on use of OutputString(): Strings with backslashes (\) need lots
+ * of backslashes. You have to escape them for the C compiler, and escape
+ * them again for OutputString() itself.
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/tables.c,v 1.11 1996/06/15 03:45:02 fld Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include <regexp.h>
+#include "general.h"
+#include "translate.h"
+
+/* text width of page, in inches */
+#define TEXTWIDTH 5.5
+#define MAXCOLS 100
+#define SPAN_NOT 0
+#define SPAN_START 1
+#define SPAN_CONT 2
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+/*table parameters */
+
+#define TBLMAXCOL 30 /* max number of columns in tbl table */
+#define NAMELEN 40 /* max length of a name */
+#define BOFTTHRESHOLD 35 /* text length over which to consider
+ * generating a block of filled text */
+
+
+/* handy declarations */
+
+typedef enum { Left, Right, Center, Justify, Char, Span } tblalign;
+
+typedef enum { TGroup, THead, TFoot, TBody } tblsource; /* source of a spec */
+
+
+/* table line format information structures */
+
+struct tblcolspec {
+
+ char name[NAMELEN]; /* colspec's name */
+ short num; /* column number */
+ tblsource source; /* where defined */
+
+ tblalign align; /* column's alignment */
+ char alignchar; /* character for alignment */
+ short aligncharoff; /* offset for alignment */
+ char colwidth[10]; /* width for column */
+ char colpwidth[10]; /* proportional widths for column */
+ bool colsep; /* separator to right of column? */
+ bool rowsep; /* separator to bottom of column? */
+ short moreRows; /* value for Morerows */
+
+ struct tblcolspec * next; /* next colspec */
+};
+
+struct tblspanspec {
+
+ char name[NAMELEN]; /* spanspec's name */
+ tblsource source; /* where defined */
+
+ struct tblcolspec * start; /* start column */
+ struct tblcolspec * end; /* end column */
+ tblalign align; /* span's alignment */
+ char alignchar; /* character for alignment */
+ short aligncharoff; /* offset for alignment */
+ bool colsep; /* separator to right of column? */
+ bool rowsep; /* separator to bottom of column? */
+
+ struct tblspanspec * next; /* next spanspec */
+};
+
+struct tblformat {
+ short count; /* count of rows matching this spec */
+
+ short cols; /* # of columns */
+ short rowNum; /* row number */
+ char colformat[TBLMAXCOL]; /* per-column formats */
+ char colwidth[TBLMAXCOL][10]; /* per-column widths */
+ char colpwidth[TBLMAXCOL][10]; /* per-column proportional widths */
+ char font[TBLMAXCOL][3]; /* column fonts (headers) */
+ bool colsep[TBLMAXCOL]; /* column separators */
+ bool rowsep[TBLMAXCOL]; /* row separators */
+ short moreRows[TBLMAXCOL]; /* moreRows indicator */
+
+ struct tblformat * next; /* for the next row */
+};
+
+
+/* table state info */
+
+static short tblcols = 0; /* number of columns in the table */
+static short tblrow = 0; /* the current row in the table */
+
+static bool tblTGroupSeen = FALSE; /* seen a TGroup in this table yet? */
+
+static char * tblFrame; /* table frame info */
+static bool tblgcolsep; /* global colsep (in table) */
+static bool tblgrowsep; /* global rowsep (in table) */
+
+static int tblBOFTCount = 0; /* count of bofts that we've created
+ * (per table) */
+int BOFTTextThresh = BOFTTHRESHOLD;
+ /* length of text before we
+ * call it a BOFT */
+static bool tblboft = FALSE; /* within a block of filled text? */
+static bool tblinBOFT = FALSE; /* within a boft now? */
+
+static struct tblformat * formP = 0; /* THead/TBody format lines */
+
+static struct tblcolspec * tblColSpec = 0; /* colspec structure for table */
+static struct tblspanspec * tblSpanSpec = 0; /* spanspec structure for table */
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+
+/* these cover the attributes on the Table, TGroup, Colspec elements */
+typedef struct {
+ char *cols;
+ char *align, **align_v;
+ char *colwidth, **colwidth_v;
+ char *colsep, **colsep_v;
+ char *rowsep, **rowsep_v;
+ char *frame;
+ char *orient;
+ int pgwide;
+ int n_align, n_model, n_colwidth, n_colsep;
+ int nc;
+} TableInfo;
+
+
+/* some flags, set when the table tag is processed, used later */
+static int rowsep, siderules;
+static int frametop, framebot, frameall;
+static char basemodel[128]; /* model for table (in formatting language) */
+static int spaninfo[MAXCOLS]; /* 100 columns, max */
+static TableInfo TheTab;
+
+/* forward references */
+void SetTabAtts(Element_t *, TableInfo *, int);
+void FreeTabAtts(TableInfo *);
+void ClearTable(TableInfo *);
+void CheckTable(Element_t *);
+void TblTStart(Element_t *, FILE *);
+void TblTEnd(Element_t *, FILE *);
+void TblTGroup(Element_t *, FILE *);
+void TblTGroupEnd(Element_t *, FILE *);
+void TblTFoot(Element_t *, FILE *);
+void TblBuildFormat(Element_t *, struct tblformat **, tblsource);
+struct tblformat * TblBuild1Format(Element_t *, bool, tblsource);
+char TblGetAlign(short, Element_t *, tblsource);
+char * TblGetWidth(short, Element_t *, bool, tblsource);
+char * TblGetFont(short, Element_t *, tblsource);
+bool TblGetColSep(short, Element_t *, tblsource);
+bool TblGetRowSep(short, Element_t *, tblsource);
+short TblGetMoreRows(short, Element_t *, tblsource);
+bool TblColAdv(short, Element_t *, struct tblformat *, tblsource);
+struct tblcolspec * TblEntryColSpec(short, Element_t *, tblsource);
+struct tblspanspec * TblEntrySpanSpec(short, Element_t *, tblsource);
+bool TblFormatMatch(struct tblformat *, struct tblformat *);
+void TblPrintFormat(FILE *, struct tblformat *);
+void TblTRowStart(Element_t *, FILE *);
+void TblTRowEnd(Element_t *, FILE *);
+void TblTCellStart(Element_t *, FILE *);
+int TblCountContent(Element_t *);
+void TblTCellEnd(Element_t *, FILE *);
+struct tblcolspec * TblDoColSpec(short, Element_t *, struct tblcolspec *, tblsource);
+struct tblspanspec * TblDoSpanSpec(Element_t *, struct tblspanspec *, tblsource);
+struct tblcolspec * TblFindColSpec(char *, tblsource);
+struct tblcolspec * TblFindColNum(short, tblsource);
+struct tblspanspec * TblFindSpanSpec(char *, tblsource);
+void TexTable(Element_t *, FILE *);
+void TexTableCellStart(Element_t *, FILE *);
+void TexTableCellEnd(Element_t *, FILE *);
+void TexTableRowStart(Element_t *, FILE *);
+void TexTableRowEnd(Element_t *, FILE *);
+void TexTableTop(Element_t *, FILE *);
+void TexTableBottom(Element_t *, FILE *);
+
+/* ______________________________________________________________________ */
+/* Hard-coded stuff for CALS-style DTD tables.
+ * Here are the TABLE attributes (for handy reference):
+ *
+ * Table/InformalTable:
+ * Colsep NUMBER separate all columns in table?
+ * Frame (Top|Bottom|Topbot|All|Sides|None) frame style
+ * Orient (Port | Land) orientation
+ * Pgwide NUMBER wide table?
+ * Rowsep NUMBER separate all rows in the table?
+ * Tabstyle NMTOKEN FOSI table style
+ *
+ * TGroup:
+ * Align (Left|Right|Center|Justify|Char) alignment of cols
+ * Char CDATA Alignment specifier
+ * Charoff NUTOKEN "" ""
+ * Cols NUMBER number of columns
+ * Colsep NUMBER separate all columns in tgroup?
+ * Rowsep NUMBER separate all rows in tgroup?
+ * TGroupstyle NMTOKEN FOSI table group style
+ *
+ * Colspec:
+ * Align (Left|Right|Center|Justify|Char) entry align
+ * Char CDATA Alignment specifier
+ * Charoff NUTOKEN "" ""
+ * Colname NMTOKEN Column identifier
+ * Colnum NUMBER number of column
+ * Colsep NUMBER separate this col from next?
+ * Colwidth CDATA width spec
+ * Rowsep NUMBER serarate entry from following row?
+ *
+ * SpanSpec:
+ * Align (Left|Right|Center|Justify|Char) entry align
+ * Char CDATA Alignment specifier
+ * Charoff NUTOKEN "" ""
+ * Colsep NUMBER separate this col from next?
+ * Nameend NMTOKEN name of rightmost col of a span
+ * Namest NMTOKEN name of leftmost col of a span
+ * Rowsep NUMBER serarate entry from following row?
+ * Spanname NMTOKEN name of a horiz. span
+ *
+ * THead/TFoot/TBody:
+ * VAlign (Top | Middle | Bottom) group placement
+ *
+ * Row:
+ * Rowsep NUMBER separate this row from next?
+ * VAlign (Top | Middle | Bottom) row placement
+ *
+ * Entry:
+ * Align (Left|Right|Center|Justify|Char) entry align
+ * Char CDATA Alignment specifier
+ * Charoff NUTOKEN "" ""
+ * Colname NMTOKEN Column identifier
+ * Colsep NUMBER separate this col from next?
+ * Morerows NUMBER number of addn'l rows in vert straddle
+ * Nameend NMTOKEN name of rightmost col of a span
+ * Namest NMTOKEN name of leftmost col of a span
+ * Rotate NUMBER 90 degree rotation counterclockwise to table?
+ * Rowsep NUMBER serarate entry from following row?
+ * Spanname NMTOKEN name of a horiz. span
+ * VAlign (Top | Middle | Bottom) text vert alignment
+ *
+ *
+ ** OBSOLETE OSF DTD FORM (still used for TeX form):
+ ** Usage in transpec: _calstable [tex|check|clear] ['aspect']
+ ** where 'aspect' is:
+ ** rowstart stuff to do at start of a row (tests for spanning)
+ ** rowend stuff to do at end of a row (eg, rules, etc.)
+ ** cellstart stuff to do at start of a cell (eg, handle actual
+ ** spanning instructions, etc.)
+ ** cellend stuff to do at end of a cell (eg, cell separator)
+ ** top stuff to do at top of the table
+ ** (like whether or not it needs a starting horiz rule)
+ ** bottom stuff to do at bottom of the table
+ ** (like whether or not it needs an ending horiz rule)
+ ** (nothing) the 'cols' param to LaTeX's \begin{tabular}[pos]{cols}
+ ** or 'options' and 'formats' part in tbl
+ *
+ *
+ * New tbl form:
+ * Usage in transpec: _calstable [tbl] ['aspect']
+ * where 'aspect' is:
+ * tablestart start a table and do style info
+ * tableend end the table and clean up
+ * tablegroup table TGroup (.T& if not 1st, line format info)
+ * tablegroupend end a TGroup
+ * tablefoot TFoot within a TGroup
+ * rowstart start of a row
+ * rowend end of a row
+ * entrystart start of an entry (block of filled text, if
+ * appropriate)
+ * entryend end of a cell (eg, cell separator)
+ */
+
+/* Procedure to
+ * Arguments:
+ * Pointer to element under consideration.
+ * FILE pointer to where to write output.
+ * Vector of args to _osftable
+ * Count of args to _osftable
+ */
+void
+CALStable(
+ Element_t *e,
+ FILE *fp,
+ char **av,
+ int ac
+)
+{
+ /* Check params and dispatch to appropriate routine */
+
+ if (!strcmp(av[1], "tbl")) {
+
+ if (ac > 2) {
+ if (!strcmp(av[2], "tablestart")) TblTStart(e, fp);
+ else if (!strcmp(av[2], "tableend")) TblTEnd(e, fp);
+ else if (!strcmp(av[2], "tablegroup")) TblTGroup(e, fp);
+ else if (!strcmp(av[2], "tablegroupend")) TblTGroupEnd(e, fp);
+ else if (!strcmp(av[2], "tablefoot")) TblTFoot(e, fp);
+ else if (!strcmp(av[2], "rowstart")) TblTRowStart(e, fp);
+ else if (!strcmp(av[2], "rowend")) TblTRowEnd(e, fp);
+ else if (!strcmp(av[2], "entrystart")) TblTCellStart(e, fp);
+ else if (!strcmp(av[2], "entryend")) TblTCellEnd(e, fp);
+ else fprintf(stderr, "Unknown %s table instruction: %s\n",
+ av[1], av[2]);
+ }
+ else {
+ fprintf(stderr, "Incomplete %s table instruction\n");
+ }
+ }
+
+ else if (!strcmp(av[1], "tex")) {
+
+ if (ac > 1 && !strcmp(av[1], "check")) CheckTable(e);
+
+ else
+ if (ac > 1 && !strcmp(av[1], "clear")) ClearTable(&TheTab);
+
+ if (ac > 2) {
+ if (!strcmp(av[2], "cellstart")) TexTableCellStart(e, fp);
+ else if (!strcmp(av[2], "cellend")) TexTableCellEnd(e, fp);
+ else if (!strcmp(av[2], "rowstart")) TexTableRowStart(e, fp);
+ else if (!strcmp(av[2], "rowend")) TexTableRowEnd(e, fp);
+ else if (!strcmp(av[2], "top")) TexTableTop(e, fp);
+ else if (!strcmp(av[2], "bottom")) TexTableBottom(e, fp);
+ else fprintf(stderr, "Unknown %s table instruction: %s\n",
+ av[1], av[2]);
+ }
+ else TexTable(e, fp);
+ }
+
+ else fprintf(stderr, "Unknown table type: %s\n", av[1]);
+
+}
+
+/* ClearTable -- start a new table process
+ *
+ */
+
+
+void
+ClearTable( TableInfo * t )
+{
+ memset(t, 0, sizeof(TableInfo));
+}
+
+
+/* ______________________________________________________________________ */
+/* Set values of the our internal table structure based on the table's
+ * attributes. (This is called for tables, tgroups, colspecs, and rows,
+ * since tables and rows share many of the same attributes.)
+ * Arguments:
+ * Pointer to element under consideration.
+ * Pointer table info structure which will be filled in.
+ * Flag saying whether or not to set global variables based on attrs.
+ */
+void
+SetTabAtts(
+ Element_t *e,
+ TableInfo *t,
+ int set_globals
+)
+{
+ char *at;
+ Element_t * ep;
+
+ /* remember values of attributes */
+ if ((at = FindAttValByName(e, "ALIGN"))) t->align = at;
+ if ((at = FindAttValByName(e, "COLWIDTH"))) t->colwidth = at;
+ if ((at = FindAttValByName(e, "COLSEP"))) t->colsep = at;
+ if ((at = FindAttValByName(e, "FRAME"))) t->frame = at;
+ if ((at = FindAttValByName(e, "COLS"))) t->cols = at;
+
+ /* Set some things for later when processing this table */
+ if (set_globals) {
+
+ rowsep = 1;
+ frametop = framebot = 1; /* default style */
+
+ /* For now we look at the first number of rowsep - it controls the
+ * horiz rule for then entire row. (not easy to specify lines that
+ * span only some columns in tex or tbl. */
+ if ((at = FindAttValByName(e, "ROWSEP"))) rowsep = atoi(at);
+ }
+
+ if (t->frame) {
+ /* Top|Bottom|Topbot|All|Sides|None */
+ if (!strcmp(t->frame, "NONE") || !strcmp(t->frame, "SIDES"))
+ frametop = framebot = 0;
+ else if (!strcmp(t->frame, "TOP")) framebot = 0;
+ else if (!strcmp(t->frame, "BOTTOM")) frametop = 0;
+ }
+
+ /* tbl and tex like lower case for units. convert. */
+ if (t->colwidth) {
+ char *cp;
+ for (cp=t->colwidth; *cp; cp++)
+ if (isupper(*cp)) *cp = tolower(*cp);
+ }
+
+ /* Now, split (space-separated) strings into vectors. Hopefully, the
+ * number of elements in each vector matches the number of columns.
+ */
+ t->align_v = Split(t->align, &t->n_align, S_STRDUP|S_ALVEC);
+ t->colwidth_v = Split(t->colwidth, &t->n_colwidth, S_STRDUP|S_ALVEC);
+ t->colsep_v = Split(t->colsep, &t->n_colsep, S_STRDUP|S_ALVEC);
+
+ /* Determin the _numeric_ number of columns, "nc". MUST be specified
+ * in Cols attribute of TGroup element.
+ */
+ if (t->cols) t->nc = atoi(t->cols);
+}
+
+/* ______________________________________________________________________ */
+
+/* Free the storage of info use by the table info structure. (not the
+ * structure itself, but the strings its elements point to)
+ * Arguments:
+ * Pointer table info structure to be freed.
+ */
+void
+FreeTabAtts(
+ TableInfo *t
+)
+{
+ if (!t) return;
+ if (t->align_v) free(*t->align_v);
+ if (t->colwidth_v) free(*t->colwidth_v);
+ if (t->colsep_v) free(*t->colsep_v);
+}
+
+/* ______________________________________________________________________ */
+/* Check the attributes and children of the table pointed to by e.
+ * Report problems and inconsistencies to stderr.
+ * Arguments:
+ * Pointer to element (table) under consideration.
+ */
+
+void
+CheckTable(
+ Element_t *e
+)
+{
+ int pr_loc=0; /* flag to say if we printed location */
+ int i, r, c;
+ Element_t *ep, *ep2;
+ float wt;
+ char *tpref = "Table Check"; /* prefix for err messages */
+ char *ncolchk =
+ "Table Check: %s ('%s') has wrong number of tokens. Expecting %d.\n";
+
+ if (strcmp(e->gi, "TABLE") &&
+ strcmp(e->gi, "INFORMALTABLE") &&
+ strcmp(e->gi, "TGROUP") &&
+ strcmp(e->gi, "COLSPEC") &&
+ strcmp(e->gi, "ROW") ) {
+ fprintf(stderr, "%s: Not pointing to a table element(%s)!\n",
+ tpref, e->gi);
+ return;
+ }
+
+ FreeTabAtts(&TheTab); /* free storage, if allocated earlier */
+ SetTabAtts(e, &TheTab, 1); /* look at attributes */
+
+#if FALSE
+ /* NCOLS attribute set? */
+ if (!TheTab.ncols) {
+ pr_loc++;
+ fprintf(stderr, "%s: NCOLS attribute missing. Inferred as %d.\n",
+ tpref, TheTab.nc);
+ }
+
+ /* ALIGN attribute set? */
+ if (!TheTab.align) {
+ pr_loc++;
+ fprintf(stderr, "%s: ALIGN attribute missing.\n", tpref);
+ }
+
+ /* See if the number of cells in each row matches */
+ for (r=0; r<e->necont && (ep=e->econt[r]); r++) { /* each TGroup */
+ for (i=0; i<ep->necont && (ep2=ep->econt[i]); i++) {
+ if ( strcmp(ep2->gi, "TBODY") ) /* only TBodys */
+ continue;
+
+ for (c=0; c<ep2->necont; c++) {
+ if (ep2->econt[c]->necont != TheTab.nc) {
+ pr_loc++;
+ fprintf(stderr, "%s: COLS (%d) differs from actual number of cells (%d) in row %d.\n",
+ tpref, TheTab.nc, ep2->econt[c]->necont, c);
+ }
+ }
+ }
+ }
+#endif
+
+ /* Check ALIGN */
+ if (TheTab.align) {
+ if (TheTab.nc != TheTab.n_align) { /* number of tokens OK? */
+ pr_loc++;
+ fprintf(stderr, ncolchk, "ALIGN", TheTab.align, TheTab.nc);
+ }
+ else { /* values OK? */
+ for (i=0; i<TheTab.nc; i++) {
+ if (*TheTab.align_v[i] != 'C' && *TheTab.align_v[i] != 'L' &&
+ *TheTab.align_v[i] != 'R') {
+ pr_loc++;
+ fprintf(stderr, "%s: ALIGN (%d) value wrong: %s\n",
+ tpref, i, TheTab.align_v[i]);
+ }
+ }
+ }
+ }
+
+ /* check COLWIDTH */
+ if (TheTab.colwidth) {
+ if (TheTab.nc != TheTab.n_colwidth) { /* number of tokens OK? */
+ pr_loc++;
+ fprintf(stderr, ncolchk, "COLWIDTH", TheTab.colwidth, TheTab.nc);
+ }
+ else { /* values OK? */
+ for (i=0; i<TheTab.nc; i++) {
+
+ /* check that the units after the numbers are OK
+ we want "in", "cm".
+ */
+ }
+ }
+ }
+
+ /* check COLSEP */
+ if (TheTab.colsep) {
+ if (TheTab.nc != TheTab.n_colsep) { /* number of tokens OK? */
+ pr_loc++;
+ fprintf(stderr, ncolchk, "COLSEP", TheTab.colsep, TheTab.nc);
+ }
+ else { /* values OK? */
+ for (i=0; i<TheTab.nc; i++) {
+ }
+ }
+ }
+
+ if (pr_loc) {
+ fprintf(stderr, "%s: Above problem in table located at:\n", tpref);
+ PrintLocation(e, stderr);
+ }
+}
+
+/* ______________________________________________________________________ */
+
+/* Look at colspec attribute for spanning. If set, remember info for when
+ * doing the cells. Called by TblTableRowStart() and TexTableRowStart().
+ * Arguments:
+ * Pointer to element (row) under consideration.
+ */
+int
+check_for_spans(
+ Element_t *e
+)
+{
+ char *at;
+ char **spans;
+ int n, i, inspan;
+
+#if FALSE /* NOT IMPLEMENTED RIGHT NOW */
+
+ /* See if COLSPEC element present */
+ for (i=0; i < e->necont; i++) {
+
+ }
+
+
+ if ((at = FindAttValByName(e, "MODEL"))) {
+
+ /* Split into tokens, then look at each for the word "span" */
+ n = TheTab.nc;
+ spans = Split(at, &n, S_STRDUP|S_ALVEC);
+
+ /* Mark columns as start-of-span, in-span, or not spanned. Remember
+ * in at list, "spaningo". (Span does not make sense in 1st column.)
+ */
+ for (i=1,inspan=0; i<n; i++) {
+ if (StrEq(spans[i], "span") || StrEq(spans[i], "s")) {
+ if (inspan == 0) spaninfo[i-1] = SPAN_START;
+ spaninfo[i] = SPAN_CONT;
+ inspan = 1;
+ }
+ else {
+ spaninfo[i] = SPAN_NOT;
+ inspan = 0;
+ }
+ }
+ free(*spans); /* free string */
+ free(spans); /* free vector */
+ spaninfo[TheTab.nc] = SPAN_NOT; /* after last cell */
+ return 1;
+ }
+ /* if model not set, mark all as not spanning */
+ else
+
+#endif /* NOT CURRENTLY IMPLEMENTED */
+
+ for (i=0; i<MAXCOLS; i++) spaninfo[i] = SPAN_NOT;
+ return 0;
+}
+
+/* ______________________________________________________________________ */
+/* Do the "right thing" for the table spec for TeX tables. This will
+ * generate the arg to \begin{tabular}[xxx].
+ * Arguments:
+ * Pointer to element (table) under consideration.
+ * FILE pointer to where to write output.
+ */
+void
+TexTable(
+ Element_t *e,
+ FILE *fp
+)
+{
+ int i, n;
+ float tot;
+ char *cp, wbuf[1500], **widths=0, **widths_v=0;
+
+ FreeTabAtts(&TheTab); /* free storage, if allocated earlier */
+ SetTabAtts(e, &TheTab, 1); /* look at attributes */
+ SetTabAtts(e->econt[0], &TheTab, 1); /* attrs of TGroup */
+
+ /* Figure out the widths, based either on "colwidth".
+ */
+ if (TheTab.colwidth && TheTab.nc == TheTab.n_colwidth) {
+ widths = TheTab.colwidth_v;
+ }
+
+ siderules = 1;
+ if (TheTab.frame)
+ if (strcmp(TheTab.frame, "ALL") && strcmp(TheTab.frame, "SIDES"))
+ siderules = 0;
+
+ if (siderules) OutputString("|", fp, 1);
+ for (i=0; i<TheTab.nc; i++) {
+ /* If width specified, use it; else if align set, use it; else left. */
+ if (widths && widths[i][0] != '0' && widths[i][1] != EOS) {
+ fprintf(fp, "%sp{%s}", (i?" ":""), widths[i]);
+ }
+ else if (TheTab.align && TheTab.nc == TheTab.n_align) {
+ fprintf(fp, "%s%s", (i?" ":""), TheTab.align_v[i]);
+ }
+ else
+ fprintf(fp, "%sl", (i?" ":""));
+ /* See if we want column separators. */
+ if (TheTab.colsep) {
+
+ if ( (i+1) < TheTab.nc ) {
+ if ( *TheTab.colsep_v[i] == '1' ) {
+ fprintf(fp, " |");
+ }
+ if ( *TheTab.colsep_v[i] == '2' ) {
+ fprintf(fp, " ||");
+ }
+ }
+
+ }
+ }
+ if (siderules) OutputString("|", fp, 1);
+
+ if (widths_v) free(widths_v);
+}
+
+/*
+ * Arguments:
+ * Pointer to element (cell) under consideration.
+ * FILE pointer to where to write output.
+ */
+void
+TexTableCellStart(
+ Element_t *e,
+ FILE *fp
+)
+{
+ int n, i;
+ char buf[50], *at;
+
+ if (spaninfo[e->my_eorder] == SPAN_START) {
+ for (i=e->my_eorder+1,n=1; ; i++) {
+ if (spaninfo[i] == SPAN_CONT) n++;
+ else break;
+ }
+ sprintf(buf, "\\\\multicolumn{%d}{%sc%s}", n,
+ (siderules?"|":""), (siderules?"|":""));
+ OutputString(buf, fp, 1);
+ }
+#ifdef New
+ if ((at = FindAttValByName(e->parent, "ALIGN"))) {
+ /* no span, but user wants to change the alignment */
+ h_v = Split(wbuf, 0, S_ALVEC|S_STRDUP);
+ OutputString("\\\\multicolumn{1}{%sc%s}", n,
+ fp, 1);
+ }
+#endif
+
+ if (spaninfo[e->my_eorder] != SPAN_CONT) OutputString("{", fp, 1);
+}
+
+/*
+ * Arguments:
+ * Pointer to element (cell) under consideration.
+ * FILE pointer to where to write output.
+ */
+void
+TexTableCellEnd(
+ Element_t *e,
+ FILE *fp
+)
+{
+ if (spaninfo[e->my_eorder] != SPAN_CONT) OutputString("} ", fp, 1);
+
+ /* do cell/col separators */
+ if (e->my_eorder < (TheTab.nc-1)) {
+ if (spaninfo[e->my_eorder] == SPAN_NOT ||
+ spaninfo[e->my_eorder+1] != SPAN_CONT)
+ OutputString("& ", fp, 1);
+ }
+}
+
+/* Look at model for spanning. If set, remember it for when doing the cells.
+ * Arguments:
+ * Pointer to element (row) under consideration.
+ * FILE pointer to where to write output.
+ */
+void
+TexTableRowStart(
+ Element_t *e,
+ FILE *fp
+)
+{
+ check_for_spans(e);
+}
+
+/*
+ * Arguments:
+ * Pointer to element (row) under consideration.
+ * FILE pointer to where to write output.
+ */
+void
+TexTableRowEnd(
+ Element_t *e,
+ FILE *fp
+)
+{
+ char *at;
+
+ /* check this row's attributes */
+ if ((at = FindAttValByName(e, "ROWSEP"))) {
+ if (at[0] == '1') OutputString("\\\\\\\\[2mm] \\\\hline ", fp, 1);
+ }
+ else if (rowsep) OutputString("\\\\\\\\ ", fp, 1);
+ else
+ OutputString("\\\\\\\\ ", fp, 1);
+
+}
+
+/*
+ * Arguments:
+ * Pointer to element (table) under consideration.
+ * FILE pointer to where to write output.
+ */
+void
+TexTableTop(Element_t *e, FILE *fp)
+{
+ if (frametop) OutputString("\\\\hline", fp, 1);
+}
+
+void
+TexTableBottom(Element_t *e, FILE *fp)
+{
+ if (framebot) OutputString("\\\\hline", fp, 1);
+}
+
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+/* ___________________________| |____________________________ */
+/* ___________________________| TBL STUFF |____________________________ */
+/* ___________________________| |____________________________ */
+/* ___________________________|_____________|____________________________ */
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+/* ______________________________________________________________________ */
+
+
+
+/* TblTStart() -- start a table and do style information
+ *
+ * TO DO:
+ *
+ * do .TS
+ * find global rowsep and colsep
+ */
+
+
+void
+TblTStart(Element_t * ep,
+ FILE * fP)
+{
+ register char * cp;
+ register struct Element_t * ep2;
+
+
+
+ OutputString("^.TS^", fP, 1);
+
+ tblTGroupSeen = FALSE;
+ tblinBOFT = FALSE; /* within a boft? */
+ tblBOFTCount = 0; /* count of Blocks of Filled Text that
+ * we've created */
+
+ tblgcolsep = (cp = FindAttValByName(ep, "COLSEP")) && !strcmp(cp, "1");
+ tblgrowsep = (cp = FindAttValByName(ep, "ROWSEP")) && !strcmp(cp, "1");
+}
+
+/* TblTEnd() -- end a table and do any cleanup
+ *
+ * TO DO:
+ *
+ * do .TE
+ *
+ * deallocate format line info
+ */
+
+
+
+void
+TblTEnd(Element_t * ep,
+ FILE * fP)
+{
+ register struct tblformat * ffp, * ffp2;
+
+
+ if ( tblBOFTCount > 31 ) {
+ fprintf(stderr, "# warning, line %d: created %d blocks of filled text in one table\n",
+ ep->lineno, tblBOFTCount);
+ fprintf(stderr, "#\t\t(31 is the limit in some systems)\n");
+ }
+
+ OutputString("^.TE^", fP, 1);
+
+ for ( ffp=formP; ffp; ffp=ffp2 ) {
+ ffp2 = ffp->next;
+ free(ffp); /* clear entire list */
+ }
+ formP = 0;
+}
+
+/* TblTTGroup() -- do body work (row format info)
+ *
+ * TO DO:
+ *
+ * set number of columns
+ *
+ * if this is the first TGroup of this table, do style info:
+ * a. alignment
+ * b. defaults: tab
+ * c. box vx allbox
+ *
+ * do format info:
+ * a. generate tableformat structure
+ * b. output it
+ *
+ * prepare structures for colspecs and spanspecs
+ *
+ */
+
+
+
+void
+TblTGroup(Element_t * ep,
+ FILE * fP)
+{
+ register int i, j, k;
+ register char * cp, * cp2;
+ register Element_t * ep2, ep3;
+ register struct tblcolspec * tcsp, * tcsp2;
+ register struct tblspanspec * tssp, * tssp2;
+
+
+ tblColSpec = 0; /* make sure they're clear */
+ tblSpanSpec = 0;
+
+ /* set the number of columns */
+
+ tblcols = atoi(FindAttValByName(ep, "COLS"));
+
+ /* do colspecs */
+
+ tblColSpec = tcsp = TblDoColSpec(0, ep, 0, TGroup);
+ /* do TGroup first -- it becomes the default */
+
+ for ( i=0, k=1; i < ep->necont; i++ ) {
+
+ if ( !strcmp(ep->econt[i]->gi, "COLSPEC") ) {
+ tcsp2 = TblDoColSpec(k, ep->econt[i], tblColSpec, TGroup);
+ tcsp->next = tcsp2; /* put into list */
+ tcsp = tcsp2;
+ k = tcsp2->num + 1; /* next column number */
+ }
+
+ if ( !strcmp(ep->econt[i]->gi, "THEAD") ) {
+ ep2 = ep->econt[i];
+ for ( j=0, k=1; j < ep2->necont; j++ ) {
+ if ( !strcmp(ep2->econt[j]->gi, "COLSPEC") ) {
+ tcsp2 = TblDoColSpec(k, ep2->econt[j], tblColSpec, THead);
+ tcsp->next = tcsp2; /* put into list */
+ tcsp = tcsp2;
+ k = tcsp2->num + 1; /* next column number */
+ }
+ }
+ }
+
+ if ( !strcmp(ep->econt[i]->gi, "TFOOT") ) {
+ ep2 = ep->econt[i];
+ for ( j=0, k=1; j < ep2->necont; j++ ) {
+ if ( !strcmp(ep2->econt[j]->gi, "COLSPEC") ) {
+ tcsp2 = TblDoColSpec(k, ep2->econt[j], tblColSpec, TFoot);
+ tcsp->next = tcsp2; /* put into list */
+ tcsp = tcsp2;
+ k = tcsp2->num + 1; /* next column number */
+ }
+ }
+ }
+
+ if ( !strcmp(ep->econt[i]->gi, "TBODY") ) {
+ ep2 = ep->econt[i];
+ for ( j=0, k=1; j < ep2->necont; j++ ) {
+ if ( !strcmp(ep2->econt[j]->gi, "COLSPEC") ) {
+ tcsp2 = TblDoColSpec(k, ep2->econt[j], tblColSpec, TBody);
+ tcsp->next = tcsp2; /* put into list */
+ tcsp = tcsp2;
+ k = tcsp2->num + 1; /* next column number */
+ }
+ }
+ }
+ }
+
+ /* do spanspecs */
+
+ tblSpanSpec = tssp = TblDoSpanSpec(ep, 0, TGroup);
+ /* do TGroup first -- it becomes the default */
+
+ for ( i=0; i < ep->necont; i++ ) {
+ if ( !strcmp(ep->econt[i]->gi, "SPANSPEC") ) {
+ tssp2 = TblDoSpanSpec(ep->econt[i], tblSpanSpec, TGroup);
+ tssp->next = tssp2; /* put into list */
+ tssp = tssp2;
+ }
+ }
+
+
+ /* if this is the first TGroup in this table, do style stuff */
+
+ if ( ! tblTGroupSeen ) {
+
+ OutputString("tab(\007)", fP, 1);
+
+ ep2 = ep->parent;
+ if ( ! (tblFrame = FindAttValByName(ep2, "FRAME")) )
+ tblFrame = "";
+
+ if ( !strcmp(tblFrame, "ALL") ) {
+ if ( tcsp->colsep && tcsp->rowsep )
+ OutputString(" allbox", fP, 1);
+ else
+ OutputString(" box", fP, 1);
+ }
+
+ if ( (cp = FindAttValByName(ep, "ALIGN")) &&
+ !strcmp(cp, "CENTER") ) {
+ OutputString(" center", fP, 1);
+ }
+
+ OutputString(";\n", fP, 1);
+
+ tblTGroupSeen = TRUE;
+ }
+
+
+ /* do format stuff -- step through all THead rows then all TBody
+ * rows. Build a list of tblformats that describe all of them.
+ * then output the resulting list.
+ */
+
+ for ( i=0; i < ep->necont; i++ ) {
+ if ( !strcmp(ep->econt[i]->gi, "THEAD") ) {
+ TblBuildFormat(ep->econt[i], &formP, THead);
+ /* add in those rows */
+ break;
+ }
+ }
+
+ for ( i=0; i < ep->necont; i++ ) {
+ if ( !strcmp(ep->econt[i]->gi, "TBODY") ) {
+ TblBuildFormat(ep->econt[i], &formP, TBody);
+ /* add in those rows */
+ break;
+ }
+ }
+
+ TblPrintFormat(fP, formP);
+
+ tblrow = 0; /* the current row within this format */
+}
+
+/* TblTGroupEnd() -- end a TGroup
+ *
+ * TO DO:
+ *
+ * deallocate colspecs and spanspecs
+ */
+
+
+void
+TblTGroupEnd(Element_t * ep,
+ FILE * fP)
+{
+ register struct tblcolspec * tcsp, * tcsp2;
+ register struct tblspanspec * tssp, * tssp2;
+
+
+ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp2 ) {
+ tcsp2 = tcsp->next;
+ free(tcsp);
+ }
+ for ( tssp=tblSpanSpec; tssp; tssp=tssp2 ) {
+ tssp2 = tssp->next;
+ free(tssp);
+ }
+}
+
+/* TblTTFoot() -- do body foot work (row format info)
+ *
+ * TO DO:
+ *
+ * do format info:
+ * a. generate tableformat structure
+ * i. if it is only 1 line long and matches the
+ * prevailing format, just output rows.
+ * ii. else, output a .T& and the new format specs
+ */
+
+
+
+void
+TblTFoot(Element_t * ep,
+ FILE * fP)
+{
+ register struct tblformat * ffp, * ffp2;
+ static struct tblformat * tfp, * tfp2;
+
+
+ TblBuildFormat(ep, &tfp, TFoot); /* gen format for the foot */
+
+ for ( tfp2=formP; tfp2 && tfp2->next; tfp2=tfp2->next )
+ ;
+
+ if ( tfp->next || !TblFormatMatch(tfp, tfp2) ) {
+
+ for ( ffp=formP; ffp; ffp=ffp2 ) {
+ ffp2 = ffp->next;
+ free(ffp); /* clear entire list */
+ }
+
+ formP = tfp; /* this becomes the prevailing format */
+
+ OutputString("^.T&^", fP, 1);
+ TblPrintFormat(fP, formP);
+ }
+
+ tblrow = 0; /* the current row within this format */
+}
+
+/* TblBuildFormat() -- build a format structure out of a set of
+ * rows and columns
+ *
+ */
+
+
+void
+TblBuildFormat(Element_t * ep, /* parent of rows.. */
+ struct tblformat ** fp, /* pointer to head of struct we're
+ * building */
+ tblsource source) /* type of record */
+{
+ register int i;
+ register struct tblformat * lfp; /* "current" format */
+ register struct tblformat * nfp; /* the next format */
+
+
+ for ( lfp= *fp; lfp && lfp->next; lfp=lfp->next )
+ ; /* find end of format list */
+
+ for ( i=0; i < ep->necont; i++ )
+ if ( !strcmp(ep->econt[i]->gi, "ROW") )
+ break; /* find where rows start */
+
+ for ( ; i < ep->necont; i++ ) {
+
+ nfp = TblBuild1Format(ep->econt[i], FALSE, source);
+ /* do one row */
+
+ if ( !lfp )
+ lfp = *fp = nfp; /* first one */
+ else
+ if ( TblFormatMatch(lfp, nfp) )
+ lfp->count++; /* matches */
+ else {
+ lfp->count = 1; /* only 1 so far */
+ lfp->next = nfp; /* new one */
+ lfp = nfp;
+ }
+ }
+}
+
+/* TblBuild1Format() -- build one row's worth of format information
+ *
+ */
+
+
+
+struct tblformat *
+TblBuild1Format(Element_t * rp, /* the row to deal with */
+ bool addinRowsep, /* insert rowsep into model? */
+ tblsource source) /* type type of row */
+{
+ register int i;
+ register bool allProp;
+ float totalProp;
+ register struct tblformat * tfp;
+ register Element_t * ep; /* entry pointer */
+
+
+ Calloc(1, tfp, struct tblformat);
+ tfp->cols = tblcols;
+ ep = (rp->necont) ? rp->econt[0] : 0; /* first entry */
+ allProp = TRUE;
+ totalProp = 0;
+
+ for ( i=1; i <= tblcols; i++ ) {
+ tfp->colformat[i] = TblGetAlign(i, ep, source);
+ strcpy(tfp->colwidth[i], TblGetWidth(i, ep, TRUE, source));
+ strcpy(tfp->colpwidth[i], TblGetWidth(i, ep, FALSE, source));
+ if ( allProp ) {
+ allProp = tfp->colpwidth[i][0];
+ totalProp += atof(tfp->colpwidth[i]);
+ }
+ strcpy(tfp->font[i], TblGetFont(i, ep, source));
+ tfp->colsep[i] = tblgcolsep || TblGetColSep(i, ep, source);
+ if ( addinRowsep )
+ tfp->rowsep[i] = tblgrowsep || TblGetRowSep(i, ep, source);
+ tfp->moreRows[i] = TblGetMoreRows(i, ep, source);
+
+ if ( (i < rp->necont) && TblColAdv(i, ep, tfp, source) ) {
+ ep = rp->econt[i];
+ }
+ }
+
+ /* turn proportional widths into real widths */
+
+ if ( allProp ) {
+ for ( i=1; i <= tblcols; i++ ) {
+ sprintf(tfp->colwidth[i], "%fi",
+ (atof(tfp->colpwidth[i]) / totalProp) * TEXTWIDTH);
+ }
+ }
+
+ return tfp;
+}
+
+/* TblGetAlign() -- get alignment spec for a entry
+ *
+ */
+
+
+char
+TblGetAlign(short col, /* column number */
+ Element_t * entry, /* the entry */
+ tblsource source) /* context */
+{
+ register struct tblcolspec * tcsp;
+ register struct tblspanspec * tssp;
+ register tblalign talign;
+
+
+ if ( entry && (tssp = TblEntrySpanSpec(col, entry, source)) ) {
+ talign = tssp->align;
+ free(tssp);
+ } else
+ if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) ) {
+ talign = tcsp->align;
+ free(tcsp);
+ } else {
+ return 'l';
+ }
+
+ switch ( talign ) {
+ case Left: return 'l';
+ case Right: return 'r';
+ case Center: return 'c';
+ case Justify: return 'l';
+ case Char: return 'd';
+ case Span: return 's';
+ }
+}
+
+/* TblGetWidth() -- get width spec, if any, for a entry
+ *
+ */
+
+
+char *
+TblGetWidth(short col, /* column number */
+ Element_t * entry, /* the entry */
+ bool literal, /* literal (or proportional) */
+ tblsource source) /* context */
+{
+ register struct tblcolspec * tcsp;
+ register struct tblspanspec * tssp;
+ static char colWidth[10];
+
+
+ colWidth[0] = 0;
+
+ if ( entry &&
+ (tcsp = TblEntryColSpec(col, entry, source)) &&
+ tcsp->colwidth[0] ) {
+
+ if ( !strstr(tcsp->colwidth, "*") ) {
+ if ( literal )
+ strcpy(colWidth, tcsp->colwidth);
+ } else {
+ if ( ! literal )
+ strcpy(colWidth, tcsp->colwidth);
+ }
+ free(tcsp);
+ }
+
+ return colWidth;
+}
+
+/* TblGetFont() -- get font spec, if any, for a entry
+ *
+ */
+
+
+char *
+TblGetFont(short col, /* column number */
+ Element_t * entry, /* the entry */
+ tblsource source) /* context */
+{
+ register struct tblcolspec * tcsp;
+ register struct tblspanspec * tssp;
+
+
+ return "";
+}
+
+/* TblGetColSep() -- get column separater spec, if any, for a entry
+ *
+ */
+
+
+bool
+TblGetColSep(short col, /* column number */
+ Element_t * entry, /* the entry */
+ tblsource source) /* context */
+{
+ register struct tblcolspec * tcsp;
+ register struct tblspanspec * tssp;
+ register bool colsep;
+
+
+ if ( entry && (tssp = TblEntrySpanSpec(col, entry, source)) ) {
+ colsep = tssp->colsep;
+ free(tssp);
+ } else
+ if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) ) {
+ colsep = tcsp->colsep;
+ free(tcsp);
+ } else
+ colsep = FALSE;
+
+ return colsep;
+}
+
+/* TblGetRowSep() -- get row separater spec, if any, for a entry
+ *
+ */
+
+
+bool
+TblGetRowSep(short col, /* column number */
+ Element_t * entry, /* the entry */
+ tblsource source) /* context */
+{
+ register struct tblcolspec * tcsp;
+ register struct tblspanspec * tssp;
+ register bool rowsep;
+
+ if ( entry && (tssp = TblEntrySpanSpec(col, entry, source)) ) {
+ rowsep = tssp->rowsep;
+ free(tssp);
+ } else
+ if ( entry && (tcsp = TblEntryColSpec(col, entry, source)) ) {
+ rowsep = tcsp->rowsep;
+ free(tcsp);
+ } else {
+ rowsep = FALSE;
+ }
+
+ return rowsep;
+}
+
+/* TblGetmoreRows() -- get moreRows value
+ *
+ */
+
+
+bool
+TblGetMoreRows(short col, /* column number */
+ Element_t * entry, /* the entry */
+ tblsource source) /* context */
+{
+ register char * cp;
+
+
+ if ( cp = FindAttValByName(entry, "MOREROWS") )
+ return atoi(cp);
+ else
+ return 0;
+}
+
+/* TblColAdv() -- advance pointer to next entry, if appropriate
+ *
+ */
+
+
+bool
+TblColAdv(short col, /* the current column */
+ Element_t *ep, /* pointer to entry */
+ struct tblformat * tfp, /* pointer to prevailing format */
+ tblsource source) /* context */
+{
+ register bool bump;
+ register struct tblspanspec * tssp;
+
+
+ bump = TRUE;
+
+ if ( tssp = TblEntrySpanSpec(col, ep, source) ) {
+ bump = tssp->align != Span;
+ free(tssp);
+ }
+
+ return bump;
+}
+
+/* TblEntryColSpec() -- get a completely localized colspec for an entry
+ *
+ */
+
+
+struct tblcolspec *
+TblEntryColSpec(short num, /* column number */
+ Element_t * ep, /* entry */
+ tblsource source) /* context */
+{
+ register int i;
+ register bool throwAway;
+ register char * cp;
+ register struct tblcolspec * tcsp, * tcsp2;
+
+
+ tcsp = tcsp2 = 0;
+ throwAway = FALSE;
+
+ if ( (cp = FindAttValByName(ep, "COLNAME")) ) {
+ if ( ! (tcsp = TblFindColSpec(cp, source)) ) {
+ fprintf(stderr, "? can't find column name '%s'\n", cp);
+ }
+ }
+
+ if ( tcsp2 = TblFindColNum(num, source) ) {
+ tcsp = TblDoColSpec(num, ep, tcsp2, source);
+ throwAway = TRUE;
+ }
+
+ tcsp2 = TblDoColSpec(num, ep, tcsp, source);
+
+ if ( throwAway )
+ free(tcsp);
+
+ return tcsp2;
+}
+
+/* TblEntrySpanSpec() -- get a completely localized spanspec for an entry
+ *
+ */
+
+
+struct tblspanspec *
+TblEntrySpanSpec(short num, /* column number */
+ Element_t * ep, /* entry */
+ tblsource source) /* context */
+{
+ register char * cp, * cp2;
+ register struct tblspanspec * tssp, * tssp2;
+
+
+ tssp2 = 0;
+
+ if ( !(cp = FindAttValByName(ep, "SPANNAME")) ||
+ !(tssp2 = TblFindSpanSpec(cp, source)) ) {
+
+ if ( !FindAttValByName(ep, "NAMEST") )
+ return 0;
+ }
+
+ tssp = TblDoSpanSpec(ep, tssp2, source);
+
+ if ( tssp->start && tssp->end &&
+ (tssp->start->num < num) && (tssp->end->num >= num) ) {
+ tssp->align = Span;
+ }
+
+ return tssp;
+}
+
+/* TblFormatMatch() -- compare two format rows for consistency
+ *
+ */
+
+
+bool
+TblFormatMatch(struct tblformat * tf1, /* one row */
+ struct tblformat * tf2) /* the other */
+{
+ register int i;
+
+ if ( tf1->cols != tf2->cols ) {
+ return FALSE;
+ }
+
+ for ( i=0; i < tf1->cols; i++ ) {
+
+ if ( tf1->colformat[i] != tf2->colformat[i] ) {
+ return FALSE;
+ }
+ if ( strcmp(tf1->colwidth[i], tf2->colwidth[i]) ) {
+ return FALSE;
+ }
+ if ( strcmp(tf1->font[i], tf2->font[i]) ) {
+ return FALSE;
+ }
+ if ( tf1->colsep[i] != tf2->colsep[i] ) {
+ return FALSE;
+ }
+ if ( tf1->rowsep[i] != tf2->rowsep[i] ) {
+ return FALSE;
+ }
+ if ( tf1->moreRows[i] || tf2->moreRows[i] ) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* TblPrintFormat() -- print a tbl format structure
+ *
+ */
+
+
+void
+TblPrintFormat(FILE * fP, /* where to print */
+ struct tblformat * tfp) /* the structure */
+{
+ register int i;
+ register struct tblformat * tfp2, * tfp3;
+ static char buf[3] = "\000\000";
+
+
+ for ( tfp2=tfp, tfp3=0; tfp2; tfp2=tfp2->next ) {
+ for ( i=1; i <= tfp->cols; i++ ) {
+ if ( i > 1 )
+ OutputString(" ", fP, 1);
+ if ( tfp3 && tfp3->moreRows[i] )
+ OutputString("\\^", fP, 1);
+ else {
+ buf[0] = tfp2->colformat[i];
+ OutputString(buf, fP, 1);
+ }
+ if ( tfp2->colwidth[i][0] ) {
+ OutputString("w(", fP, 1);
+ OutputString(tfp2->colwidth[i], fP, 1);
+ OutputString(")", fP, 1);
+ }
+ if ( tfp2->font[i][0] )
+ OutputString(tfp2->font[i], fP, 1);
+ if ( tfp2->colsep[i] )
+ OutputString("|", fP, 1);
+ }
+ if ( ! tfp2->next )
+ OutputString(".", fP, 1);
+ OutputString("^", fP, 1);
+ tfp3 = tfp2;
+ }
+}
+
+/* TblTRowStart() -- start a row (not much to do)
+ *
+ * TO DO:
+ *
+ * nothing..
+ *
+ */
+
+
+
+void
+TblTRowStart(Element_t * ep,
+ FILE * fP)
+{
+
+ /* nothing to do */
+
+ tblrow++; /* except note that we're within a new row */
+
+}
+
+/* TblTRowEnd() -- end a row
+ *
+ * TO DO:
+ *
+ * output a row end character (newline)
+ * if the current row had a rowsep, then output a "fake" row
+ * with underlines in the proper place(s).
+ */
+
+
+
+void
+TblTRowEnd(Element_t * ep,
+ FILE * fP)
+{
+ register int i, k;
+ register tblsource source;
+ register bool startedRow, didSep;
+ register struct tblformat * rfp;
+
+
+ OutputString("^", fP, 1);
+
+ /* get the format for this row */
+
+ if ( !strcmp(ep->parent->gi, "TFoot") )
+ source = TFoot;
+ else
+ if ( !strcmp(ep->parent->gi, "THead") )
+ source = THead;
+ else
+ source = TBody;
+
+ rfp = TblBuild1Format(ep, TRUE, source);
+ startedRow = FALSE;
+ didSep = FALSE;
+
+ for ( i=1; i <= formP->cols; i++ ) {
+ if ( rfp->rowsep[i] ||
+ (didSep && (rfp->colformat[i] == 's')) ) {
+ if ( ! startedRow ) {
+ OutputString("^", fP, 1);
+ for ( k=1; k < i; k++ )
+ OutputString("\007", fP, 1);
+ startedRow = TRUE;
+ }
+ OutputString("_\007", fP, 1);
+ didSep = TRUE;
+ } else {
+ if ( startedRow )
+ OutputString("\007", fP, 1);
+ }
+ didSep = FALSE;
+ }
+ free(rfp); /* clear that row.. */
+
+ if ( startedRow )
+ OutputString("^", fP, 1);
+}
+
+/* TblTEntryStart() -- start an entry (block of filled text if
+ * appropriate)
+ *
+ * TO DO:
+ *
+ * if text length > BOFTTextThresh or there is PI,
+ * then output "T{\n", else do nothing
+ *
+ */
+
+
+
+void
+TblTCellStart(Element_t * ep,
+ FILE * fP)
+{
+ register int i;
+ register Element_t * ep2;
+ register bool sawPI;
+
+
+ for ( i=0, sawPI=FALSE; (i < ep->ncont) && !sawPI; i++ )
+ if ( ep->cont[i].type == '?' )
+ sawPI = TRUE;
+
+ if ( sawPI || (TblCountContent(ep) > BOFTTextThresh) ) {
+ tblBOFTCount++;
+ OutputString("T{^", fP, 1);
+ tblinBOFT = TRUE; /* within a boft now */
+ }
+}
+
+/* TblCountContent() -- count all content below the given element
+ *
+ *
+ */
+
+
+
+int
+TblCountContent(Element_t * ep) /* the element to look under */
+{
+ register int i, count;
+
+
+ count = 0;
+
+ for ( i=0; i < ep->ncont; i++ ) {
+ if ( ep->cont[i].type == '-' ) {
+ count += strlen(ep->cont[i].ch.data);
+ } else
+ if ( ep->cont[i].type == '(' ) {
+ count += TblCountContent(ep->cont[i].ch.elem);
+ }
+ }
+
+ return count;
+}
+
+/* TblTEntryEnd() -- end an entry
+ *
+ * TO DO:
+ *
+ * if within BOFT, output "T}"
+ * if not last entry, output tab character
+ *
+ */
+
+
+
+void
+TblTCellEnd(Element_t * ep,
+ FILE * fP)
+{
+ register Element_t * ep2;
+
+
+ if ( tblinBOFT ) {
+ OutputString("^T}", fP, 1);
+ tblinBOFT = FALSE; /* back out again */
+ }
+
+ for ( ep2=ep->next; ep2; ep2=ep2->next ) {
+ if ( !strcmp(ep2->gi, "ENTRY") || !strcmp(ep2->gi, "ENTRYTBL") ) {
+ OutputString("\007", fP, 1);
+ break;
+ }
+ if ( !strcmp(ep2->gi, "ROW") )
+ break;
+ }
+}
+
+/* TblDoColSpec() -- process one element to create a new colspec
+ *
+ *
+ */
+
+
+
+struct tblcolspec *
+TblDoColSpec(short number, /* this column number */
+ Element_t * ep, /* element containing colspec stuff */
+ struct tblcolspec * pcsp, /* prevailing colspec (with defaults) */
+ tblsource source) /* precedence level of the resulting spec */
+{
+ register char * cp;
+ register struct tblcolspec * tcsp;
+
+
+ Calloc(1, tcsp, struct tblcolspec);
+
+ if ( cp = FindAttValByName(ep, "COLNAME") )
+ strcpy(tcsp->name, cp);
+
+ tcsp->num = number;
+ tcsp->source = source;
+
+ if ( cp = FindAttValByName(ep, "ALIGN") ) {
+ if ( !strcmp(cp, "LEFT") ) tcsp->align = Left;
+ else if ( !strcmp(cp, "RIGHT") ) tcsp->align = Right;
+ else if ( !strcmp(cp, "CENTER") ) tcsp->align = Center;
+ else if ( !strcmp(cp, "JUSTIFY") ) tcsp->align = Justify;
+ else if ( !strcmp(cp, "CHAR") ) tcsp->align = Char;
+ } else
+ tcsp->align = ( pcsp ) ? pcsp->align : Left;
+
+ if ( cp = FindAttValByName(ep, "CHAR") )
+ tcsp->alignchar = cp[0];
+ else
+ tcsp->alignchar = ( pcsp ) ? pcsp->alignchar : 0;
+
+ if ( cp = FindAttValByName(ep, "CHAROFF") )
+ tcsp->aligncharoff = atoi(cp);
+ else
+ tcsp->aligncharoff = ( pcsp ) ? pcsp->aligncharoff : 0;
+
+ if ( cp = FindAttValByName(ep, "COLWIDTH") )
+ strcpy(tcsp->colwidth, cp);
+ else
+ strcpy(tcsp->colwidth, ( pcsp ) ? pcsp->colwidth : "");
+
+ if ( cp = FindAttValByName(ep, "COLSEP") )
+ tcsp->colsep = !strcmp(cp, "1");
+ else
+ tcsp->colsep = ( pcsp ) ? pcsp->colsep : FALSE;
+
+ if ( cp = FindAttValByName(ep, "ROWSEP") )
+ tcsp->rowsep = !strcmp(cp, "1");
+ else
+ tcsp->rowsep = ( pcsp ) ? pcsp->rowsep : FALSE;
+
+ return tcsp;
+}
+
+/* TblDoSpanSpec() -- process one element to create a new spanspec
+ *
+ * Note that there's a hack inside here... NameSt and NameEnd are
+ * supposed to point at colnames, but if no colname is found, this
+ * code will look for a colnum by the same value.
+ */
+
+
+
+struct tblspanspec *
+TblDoSpanSpec(Element_t * ep, /* element containing spanspec stuff */
+ struct tblspanspec * pssp, /* prevailing spanspec (with defaults) */
+ tblsource source) /* precedence level of the resulting spec */
+{
+ register char * cp;
+ register struct tblspanspec * tssp;
+ register struct tblcolspec * tcsp;
+
+
+ Calloc(1, tssp, struct tblspanspec);
+
+ if ( cp = FindAttValByName(ep, "SPANNAME") ) strcpy(tssp->name, cp);
+ tssp->source = source;
+
+ if ( cp = FindAttValByName(ep, "NAMEST") ) {
+ if ( (tcsp = TblFindColSpec(cp, source)) ||
+ (tcsp = TblFindColNum(atoi(cp), source)) ) {
+ tssp->start = tcsp;
+ } else {
+ fprintf(stderr, "? spanspec namest points to unknown column '%s'\n", cp);
+ tssp->start = 0;
+ }
+ } else {
+ if ( pssp && pssp->start ) {
+ tssp->start = pssp->start;
+ }
+ }
+
+ if ( cp = FindAttValByName(ep, "NAMEEND") ) {
+ if ( (tcsp = TblFindColSpec(cp, source)) ||
+ (tcsp = TblFindColNum(atoi(cp), source)) ) {
+ tssp->end = tcsp;
+ } else {
+ fprintf(stderr, "? spanspec nameend points to unknown column '%s'\n", cp);
+ tssp->end = 0;
+ }
+ } else {
+ if ( pssp && pssp->end ) {
+ tssp->end = pssp->end;
+ }
+ }
+
+ if ( cp = FindAttValByName(ep, "ALIGN") ) {
+ if ( !strcmp(cp, "LEFT") ) tssp->align = Left;
+ else if ( !strcmp(cp, "RIGHT") ) tssp->align = Right;
+ else if ( !strcmp(cp, "CENTER") ) tssp->align = Center;
+ else if ( !strcmp(cp, "JUSTIFY") ) tssp->align = Justify;
+ else if ( !strcmp(cp, "CHAR") ) tssp->align = Char;
+ } else {
+ if ( pssp )
+ tssp->align = pssp->align;
+ }
+
+ if ( cp = FindAttValByName(ep, "CHAR") )
+ tssp->alignchar = cp[0];
+ else {
+ if ( pssp )
+ tssp->alignchar = pssp->alignchar;
+ }
+ if ( cp = FindAttValByName(ep, "CHAROFF") )
+ tssp->aligncharoff = atoi(cp);
+ else {
+ if ( pssp )
+ tssp->alignchar = pssp->alignchar;
+ }
+
+ if ( cp = FindAttValByName(ep, "COLSEP") )
+ tssp->colsep = !strcmp(cp, "1");
+ else {
+ if ( pssp )
+ tssp->colsep = pssp->colsep;
+ }
+ if ( cp = FindAttValByName(ep, "ROWSEP") )
+ tssp->rowsep = !strcmp(cp, "1");
+ else {
+ if ( pssp )
+ tssp->rowsep = pssp->rowsep;
+ }
+
+ return tssp;
+}
+
+/* TblFindColSpec() -- find a table colspec by name (colname)
+ *
+ */
+
+
+
+struct tblcolspec *
+TblFindColSpec(char * name, /* the name we're looking for */
+ tblsource source) /* the context in which to find it */
+{
+ register struct tblcolspec * tcsp;
+
+
+ /* first, try to find the one in the right "source" */
+
+ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp->next ) {
+ if ( (tcsp->source == source) && !strcmp(tcsp->name, name) )
+ return tcsp;
+ }
+
+ /* else, try to find one from a TGroup.. */
+
+ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp->next ) {
+ if ( (tcsp->source == TGroup) && !strcmp(tcsp->name, name) )
+ return tcsp;
+ }
+
+ /* else not found.. */
+
+ return 0;
+}
+
+/* TblFindColNum() -- find a table colspec by number
+ *
+ */
+
+
+
+struct tblcolspec *
+TblFindColNum(short number, /* the number we're looking for */
+ tblsource source) /* the context in which to find it */
+{
+ register struct tblcolspec * tcsp;
+
+
+
+ /* first, try to find the one in the right "source" */
+
+ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp->next ) {
+ if ( (tcsp->num == number) &&
+ ((tcsp->source == source) ||
+ ((source == THead) && (tcsp->source == TGroup))) )
+ return tcsp;
+ }
+
+ /* else, try to find one from a TGroup.. */
+
+ for ( tcsp=tblColSpec; tcsp; tcsp=tcsp->next ) {
+ if ( (tcsp->source == TGroup) && (tcsp->num == number) )
+ return tcsp;
+ }
+
+ /* else not found.. */
+
+ return 0;
+}
+
+/* TblFindSpanSpec() -- find a table spanspec by name (spanname)
+ *
+ */
+
+
+
+struct tblspanspec *
+TblFindSpanSpec(char * name, /* the name we're looking for */
+ tblsource source) /* the context in which to find it */
+{
+ register struct tblspanspec * tssp;
+
+
+ /* first, try to find the one in the right "source" */
+
+ for ( tssp=tblSpanSpec; tssp; tssp=tssp->next ) {
+ if ( !strcmp(tssp->name, name) &&
+ ((tssp->source == source) ||
+ ((source == THead) && (tssp->source == TGroup))) )
+ return tssp;
+ }
+
+ /* else not found.. */
+
+ return 0;
+}
diff --git a/usr.bin/sgmls/instant/traninit.c b/usr.bin/sgmls/instant/traninit.c
new file mode 100644
index 0000000..d3df959
--- /dev/null
+++ b/usr.bin/sgmls/instant/traninit.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 1994
+ * Open Software Foundation, Inc.
+ *
+ * Permission is hereby granted to use, copy, modify and freely distribute
+ * the software in this file and its documentation for any purpose without
+ * fee, provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation. Further, provided that the name of Open
+ * Software Foundation, Inc. ("OSF") not be used in advertising or
+ * publicity pertaining to distribution of the software without prior
+ * written permission from OSF. OSF makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Copyright (c) 1996 X Consortium
+ * Copyright (c) 1995, 1996 Dalrymple Consulting
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the X Consortium and
+ * Dalrymple Consulting shall not be used in advertising or otherwise to
+ * promote the sale, use or other dealings in this Software without prior
+ * written authorization.
+ */
+/* ________________________________________________________________________
+ *
+ * Program to manipulate SGML instances.
+ *
+ * This module contains the initialization routines for translation module.
+ * They mostly deal with reading data files (translation specs, SDATA
+ * mappings, character mappings).
+ *
+ * Entry points:
+ * ReadTransSpec(transfile) read/store translation spec from file
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/local/home/jfieber/src/cvsroot/nsgmlfmt/traninit.c,v 1.1.1.1 1996/01/16 05:14:10 jfieber Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <regexp.h>
+
+#include "general.h"
+#include "translate.h"
+
+#include "sgmls.h"
+#include "config.h"
+#include "std.h"
+
+#ifndef TRUE
+#define TRUE (1 == 1)
+#endif
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+/* forward references */
+void RememberTransSpec(Trans_t *, int);
+static void do_data(char *gi, struct sgmls_data *v, int n);
+static void build_ts(char *gi, char* cp);
+void AddCharMap(const char *from, const char* to);
+void AddSDATA(const char *from, const char *to);
+
+/* ______________________________________________________________________ */
+/* Read the translation specs from the input file, storing in memory.
+ * Arguments:
+ * Name of translation spec file.
+ */
+
+static Trans_t T;
+
+
+static
+void input_error(num, str, lineno)
+ int num;
+ char *str;
+ unsigned long lineno;
+{
+ fprintf(stderr, "Error at input line %lu: %s\n", lineno, str);
+}
+
+void
+ReadTransSpec(
+ char *transfile
+)
+{
+ FILE *fp;
+ struct sgmls *sp;
+ struct sgmls_event e;
+ char gi[LINESIZE];
+ char buf[LINESIZE];
+ char buf2[LINESIZE];
+ char *command;
+ char *sgmls = "sgmls ";
+ char maptype = '\0';
+
+ (void)sgmls_set_errhandler(input_error);
+ transfile = FilePath(transfile);
+ if (!transfile)
+ {
+ fprintf(stderr, "Error: Could not locate specified transfile\n");
+ exit(1);
+ }
+
+ /* XXX this is a quick, gross hack. Should write a parse() function. */
+ Malloc(strlen(sgmls) + strlen(transfile) + 2, command, char);
+ sprintf(command, "%s %s", sgmls, transfile);
+ fp = popen(command, "r");
+
+ sp = sgmls_create(fp);
+ while (sgmls_next(sp, &e))
+ switch (e.type) {
+ case SGMLS_EVENT_DATA:
+ do_data(gi, e.u.data.v, e.u.data.n);
+ break;
+ case SGMLS_EVENT_ENTITY:
+ fprintf(stderr, "Hm... got an entity\n");
+ break;
+ case SGMLS_EVENT_PI:
+ break;
+ case SGMLS_EVENT_START:
+ if (strncmp("RULE", e.u.start.gi, 4) == 0) {
+ /* A new transpec, so clear the data structure
+ * and look for an ID attribute.
+ */
+ struct sgmls_attribute *attr = e.u.start.attributes;
+ memset(&T, 0, sizeof T);
+ while (attr) {
+ if (attr->type == SGMLS_ATTR_CDATA
+ && strncmp("ID", attr->name, 2) == 0) {
+ strncpy(buf, attr->value.data.v->s,
+ MIN(attr->value.data.v->len, LINESIZE));
+ buf[MIN(attr->value.data.v->len, LINESIZE - 1)] = '\0';
+ T.my_id = atoi(buf);
+ }
+ attr = attr->next;
+ }
+ }
+ else if (strncmp("CMAP", e.u.start.gi, 4) == 0)
+ maptype = 'c';
+ else if (strncmp("SMAP", e.u.start.gi, 4) == 0)
+ maptype = 's';
+ else if (strncmp("MAP", e.u.start.gi, 3) == 0) {
+ struct sgmls_attribute *attr = e.u.start.attributes;
+ char *from = 0;
+ char *to = 0;
+
+ while (attr) {
+ if (attr->value.data.v && strncmp("FROM", attr->name, 4) == 0) {
+ strncpy(buf, attr->value.data.v->s,
+ MIN(attr->value.data.v->len, LINESIZE - 1));
+ buf[MIN(attr->value.data.v->len, LINESIZE - 1)] = '\0';
+ }
+ if (attr->value.data.v && strncmp("TO", attr->name, 2) == 0) {
+ strncpy(buf2, attr->value.data.v->s,
+ MIN(attr->value.data.v->len, LINESIZE - 1));
+ buf2[MIN(attr->value.data.v->len, LINESIZE - 1)] = '\0';
+ }
+ attr = attr->next;
+ }
+ if (maptype == 'c')
+ AddCharMap(buf, buf2);
+ else if (maptype == 's')
+ AddSDATA(buf, buf2);
+ else
+ fprintf(stderr, "Unknown map type!\n");
+ }
+ else {
+ strncpy(gi, e.u.start.gi, 512);
+ sgmls_free_attributes(e.u.start.attributes);
+ }
+ break;
+ case SGMLS_EVENT_END:
+ if (strncmp("RULE", e.u.start.gi, 4) == 0)
+ RememberTransSpec(&T, e.lineno);
+ 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);
+ pclose(fp);
+ free(command);
+}
+
+
+static void do_data(char *gi, struct sgmls_data *v, int n)
+{
+ int i;
+ char *cp;
+ static char *buf = 0;
+ static int buf_size = 0;
+ int buf_pos = 0;
+
+
+ /* figure out how much space this element will really
+ take, inculding expanded sdata entities. */
+
+ if (!buf)
+ {
+ buf_size = 1024;
+ Malloc(buf_size, buf, char);
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ char *s;
+ int len;
+
+ /* Mark the current position. If this is SDATA
+ we will have to return here. */
+ int tmp_buf_pos = buf_pos;
+
+ /* Make sure the buffer is big enough. */
+ if (buf_size - buf_pos <= v[i].len)
+ {
+ buf_size += v[i].len * (n - i);
+ Realloc(buf_size, buf, char);
+ }
+
+ s = v[i].s;
+ len = v[i].len;
+ for (; len > 0; len--, s++)
+ {
+ if (*s != RSCHAR) {
+ if (*s == RECHAR)
+ buf[buf_pos] = '\n';
+ else
+ buf[buf_pos] = *s;
+ buf_pos++;
+ }
+ }
+ if (v[i].is_sdata)
+ {
+ char *p;
+ buf[buf_pos] = '\0';
+ p = LookupSDATA(buf + tmp_buf_pos);
+ if (p)
+ {
+ if (buf_size - tmp_buf_pos <= strlen(p))
+ {
+ buf_size += strlen(p) * (n - i);
+ Realloc(buf_size, buf, char);
+ }
+ strcpy(buf + tmp_buf_pos, p);
+ buf_pos = tmp_buf_pos + strlen(p);
+ }
+ }
+ }
+
+ /* Clean up the trailing end of the data. */
+ buf[buf_pos] = '\0';
+ buf_pos--;
+ while (buf_pos > 0 && isspace(buf[buf_pos]) && buf[buf_pos] != '\n')
+ buf_pos--;
+ if (buf[buf_pos] == '\n')
+ buf[buf_pos] = '\0';
+
+ /* Skip over whitespace at the beginning of the data. */
+ cp = buf;
+ while (*cp && isspace(*cp))
+ cp++;
+ build_ts(gi, cp);
+}
+
+/* ______________________________________________________________________ */
+/* Set a transpec parameter
+ * Arguments:
+ * gi - the parameter to set
+ * cp - the value of the parameter
+ */
+static void build_ts(char *gi, char* cp)
+{
+ if (strcmp("GI", gi) == 0)
+ {
+ char *cp2;
+ /* if we are folding the case of GIs, make all upper (unless
+ it's an internal pseudo-GI name, which starts with '_') */
+ if (fold_case && cp[0] != '_' && cp[0] != '#')
+ {
+ for (cp2=cp; *cp2; cp2++)
+ if (islower(*cp2)) *cp2 = toupper(*cp2);
+ }
+ T.gi = AddElemName(cp);
+ }
+ else if (strcmp("START", gi) == 0)
+ T.starttext = strdup(cp);
+ else if (strcmp("END", gi) == 0)
+ T.endtext = strdup(cp);
+ else if (strcmp("RELATION", gi) == 0)
+ {
+ if (!T.relations)
+ T.relations = NewMap(IMS_relations);
+ SetMapping(T.relations, cp);
+ }
+ else if (strcmp("REPLACE", gi) == 0)
+ T.replace = strdup(cp);
+ else if (strcmp("ATTVAL", gi) == 0)
+ {
+ if (!T.nattpairs)
+ {
+ Malloc(1, T.attpair, AttPair_t);
+ }
+ else
+ Realloc((T.nattpairs+1), T.attpair, AttPair_t);
+ /* we'll split name/value pairs later */
+ T.attpair[T.nattpairs].name = strdup(cp);
+ T.nattpairs++;
+ }
+ else if (strcmp("CONTEXT", gi) == 0)
+ T.context = strdup(cp);
+ else if (strcmp("MESSAGE", gi) == 0)
+ T.message = strdup(cp);
+ else if (strcmp("DO", gi) == 0)
+ T.use_id = atoi(cp);
+ else if (strcmp("CONTENT", gi) == 0)
+ T.content = strdup(cp);
+ else if (strcmp("PATTSET", gi) == 0)
+ T.pattrset = strdup(cp);
+ else if (strcmp("VERBATIM", gi) == 0)
+ T.verbatim = TRUE;
+ else if (strcmp("IGNORE", gi) == 0)
+ {
+ if (!strcmp(cp, "all"))
+ T.ignore = IGN_ALL;
+ else if (!strcmp(cp, "data"))
+ T.ignore = IGN_DATA;
+ else if (!strcmp(cp, "children"))
+ T.ignore = IGN_CHILDREN;
+ else
+ fprintf(stderr, "Bad 'Ignore:' arg in transpec %s: %s\n",
+ gi, cp);
+ }
+ else if (strcmp("VARVAL", gi) == 0)
+ {
+ char **tok;
+ int i = 2;
+ tok = Split(cp, &i, S_STRDUP);
+ T.var_name = tok[0];
+ T.var_value = tok[1];
+ }
+ else if (strcmp("VARREVAL", gi) == 0)
+ {
+ char buf[1000];
+ char **tok;
+ int i = 2;
+ tok = Split(cp, &i, S_STRDUP);
+ T.var_RE_name = tok[0];
+ ExpandVariables(tok[1], buf, 0);
+ if (!(T.var_RE_value=regcomp(buf))) {
+ fprintf(stderr, "Regex error in VarREValue Content: %s\n",
+ tok[1]);
+ }
+ }
+ else if (strcmp("SET", gi) == 0)
+ {
+ if (!T.set_var)
+ T.set_var = NewMap(IMS_setvar);
+ SetMapping(T.set_var, cp);
+ }
+ else if (strcmp("INCR", gi) == 0)
+ {
+ if (!T.incr_var)
+ T.incr_var = NewMap(IMS_incvar);
+ SetMapping(T.incr_var, cp);
+ }
+ else if (strcmp("NTHCHILD", gi) == 0)
+ T.nth_child = atoi(cp);
+ else if (strcmp("VAR", gi) == 0)
+ SetMapping(Variables, cp);
+ else if (strcmp("QUIT", gi) == 0)
+ T.quit = strdup(cp);
+ else
+ fprintf(stderr, "Unknown translation spec (skipping it): %s\n", gi);
+
+}
+
+
+/* ______________________________________________________________________ */
+/* Store translation spec 't' in memory.
+ * Arguments:
+ * Pointer to translation spec to remember.
+ * Line number where translation spec ends.
+ */
+void
+RememberTransSpec(
+ Trans_t *t,
+ int lineno
+)
+{
+ char *cp;
+ int i, do_regex;
+ static Trans_t *last_t;
+ char buf[1000];
+
+ /* If context testing, check some details and set things up for later. */
+ if (t->context) {
+ /* See if the context specified is a regular expression.
+ * If so, compile the reg expr. It is assumed to be a regex if
+ * it contains a character other than what's allowed for GIs in the
+ * OSF sgml declaration (alphas, nums, '-', and '.').
+ */
+ for (do_regex=0,cp=t->context; *cp; cp++) {
+ if (!isalnum(*cp) && *cp != '-' && *cp != '.' && *cp != ' ') {
+ do_regex = 1;
+ break;
+ }
+ }
+
+ if (do_regex) {
+ t->depth = MAX_DEPTH;
+ if (!(t->context_re=regcomp(t->context))) {
+ fprintf(stderr, "Regex error in Context: %s\n", t->context);
+ }
+ }
+ else {
+ /* If there's only one item in context, it's the parent. Treat
+ * it specially, since it's faster to just check parent gi.
+ */
+ cp = t->context;
+ if (!strchr(cp, ' ')) {
+ t->parent = t->context;
+ t->context = NULL;
+ }
+ else {
+ /* Figure out depth of context string */
+ t->depth = 0;
+ while (*cp) {
+ if (*cp) t->depth++;
+ while (*cp && !IsWhite(*cp)) cp++; /* find end of gi */
+ while (*cp && IsWhite(*cp)) cp++; /* skip space */
+ }
+ }
+ }
+ }
+
+ /* Compile regular expressions for each attribute */
+ for (i=0; i<t->nattpairs; i++) {
+ /* Initially, name points to "name value". Split them... */
+ cp = t->attpair[i].name;
+ while (*cp && !IsWhite(*cp)) cp++; /* point past end of name */
+ if (*cp) { /* value found */
+ *cp++ = EOS; /* terminate name */
+ while (*cp && IsWhite(*cp)) cp++; /* point to value */
+ ExpandVariables(cp, buf, 0); /* expand any variables */
+ t->attpair[i].val = strdup(buf);
+ }
+ else { /* value not found */
+ t->attpair[i].val = ".";
+ }
+ if (!(t->attpair[i].rex=regcomp(t->attpair[i].val))) {
+ fprintf(stderr, "Regex error in AttValue: %s %s\n",
+ t->attpair[i].name, t->attpair[i].val);
+ }
+ }
+
+ /* Compile regular expression for content */
+ t->content_re = 0;
+ if (t->content) {
+ ExpandVariables(t->content, buf, 0);
+ if (!(t->content_re=regcomp(buf)))
+ fprintf(stderr, "Regex error in Content: %s\n",
+ t->content);
+ }
+
+ /* If multiple GIs, break up into a vector, then remember it. We either
+ * sture the individual, or the list - not both. */
+ if (t->gi && strchr(t->gi, ' ')) {
+ t->gilist = Split(t->gi, 0, S_ALVEC);
+ t->gi = NULL;
+ }
+
+ /* Now, store structure in linked list. */
+ if (!TrSpecs) {
+ Malloc(1, TrSpecs, Trans_t);
+ last_t = TrSpecs;
+ }
+ else {
+ Malloc(1, last_t->next, Trans_t);
+ last_t = last_t->next;
+ }
+ *last_t = *t;
+}
+
+
+/* ______________________________________________________________________ */
+/* Add an entry to the character mapping table, allocating or
+ * expanding the table if necessary.
+ * Arguments:
+ * Character to map
+ * String to map the character to
+ * A 'c' or an 's' for character or sdata map
+ */
+
+void
+AddCharMap(
+ const char *from,
+ const char* to
+)
+{
+ static int n_alloc = 0;
+
+ if (from && to) {
+ if (nCharMap >= n_alloc) {
+ n_alloc += 32;
+ if (!CharMap) {
+ Malloc(n_alloc, CharMap, Mapping_t);
+ }
+ else {
+ Realloc(n_alloc, CharMap, Mapping_t);
+ }
+ }
+ CharMap[nCharMap].name = strdup(from);
+ CharMap[nCharMap].sval = strdup(to);
+ nCharMap++;
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Add an entry to the SDATA mapping table.
+ * Arguments:
+ * String to map
+ * String to map to
+ */
+
+void
+AddSDATA(
+ const char *from,
+ const char *to
+)
+{
+ if (from && to) {
+ if (!SDATAmap)
+ SDATAmap = NewMap(IMS_sdata);
+ SetMappingNV(SDATAmap, from, to);
+ }
+}
+
+/* ______________________________________________________________________ */
diff --git a/usr.bin/sgmls/instant/translate.c b/usr.bin/sgmls/instant/translate.c
new file mode 100644
index 0000000..cc96b25
--- /dev/null
+++ b/usr.bin/sgmls/instant/translate.c
@@ -0,0 +1,881 @@
+/*
+ * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 1994
+ * Open Software Foundation, Inc.
+ *
+ * Permission is hereby granted to use, copy, modify and freely distribute
+ * the software in this file and its documentation for any purpose without
+ * fee, provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation. Further, provided that the name of Open
+ * Software Foundation, Inc. ("OSF") not be used in advertising or
+ * publicity pertaining to distribution of the software without prior
+ * written permission from OSF. OSF makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Copyright (c) 1996 X Consortium
+ * Copyright (c) 1995, 1996 Dalrymple Consulting
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the X Consortium and
+ * Dalrymple Consulting shall not be used in advertising or otherwise to
+ * promote the sale, use or other dealings in this Software without prior
+ * written authorization.
+ */
+/* ________________________________________________________________________
+ *
+ * Program to manipulate SGML instances.
+ *
+ * This module is for "translating" an instance to another form, usually
+ * suitable for a formatting application.
+ *
+ * Entry points for this module:
+ * DoTranslate(elem, fp)
+ * ExpandVariables(in, out, e)
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/translate.c,v 1.11 1996/06/15 22:49:00 fld Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <regexp.h>
+
+#include "general.h"
+#define STORAGE
+#include "translate.h"
+
+static Trans_t NullTrans; /* an empty one */
+
+/* forward references */
+void ProcesOutputSpec(char *, Element_t *, FILE *, int);
+static void WasProcessed(Element_t *);
+
+/* ______________________________________________________________________ */
+/* Translate the subtree starting at 'e'. Output goes to 'fp'.
+ * This is the entry point for translating an instance.
+ * Arguments:
+ * Pointer to element under consideration.
+ * FILE pointer to where to write output.
+ */
+
+void
+DoTranslate(
+ Element_t *e,
+ FILE *fp
+)
+{
+ Trans_t *t, *tn;
+
+ /* Find transpec for each node. */
+ DescendTree(e, PrepTranspecs, 0, 0, 0);
+
+ /* Stuff to do at start of processing */
+ if ((t = FindTransByName("_Start"))) {
+ if (t->starttext) ProcesOutputSpec(t->starttext, 0, fp, 1);
+ if (t->replace) ProcesOutputSpec(t->replace, 0, fp, 1);
+ if (t->message) ProcesOutputSpec(t->message, 0, stderr, 0);
+ if (t->endtext) ProcesOutputSpec(t->endtext, 0, fp, 1);
+ }
+
+ /* Translate topmost/first element. This is recursive. */
+ TransElement(e, fp, NULL);
+
+ /* Stuff to do at end of processing */
+ if ((t = FindTransByName("_End"))) {
+ if (t->starttext) ProcesOutputSpec(t->starttext, 0, fp, 1);
+ if (t->replace) ProcesOutputSpec(t->replace, 0, fp, 1);
+ if (t->message) ProcesOutputSpec(t->message, 0, stderr, 0);
+ if (t->endtext) ProcesOutputSpec(t->endtext, 0, fp, 1);
+ }
+
+ /* Warn about unprocessed elements in this doc tree, if verbose mode. */
+ if (verbose)
+ DescendTree(e, WasProcessed, 0, 0, 0);
+
+ /* Clean up. This is not yet complete, which is no big deal (since the
+ * program is normally done at this point anyway. */
+ for (t=TrSpecs; t; ) {
+ tn = t->next;
+ /* free the contents of t here ... */
+ (void)free((void* )t);
+ t = tn;
+ }
+ TrSpecs = 0;
+}
+
+/* ______________________________________________________________________ */
+/* Print warning about unprocessed elements in this doc tree (if they
+ * were not explicitely ignored).
+ * Arguments:
+ * Pointer to element under consideration.
+ */
+static void
+WasProcessed(
+ Element_t *e
+)
+{
+ Trans_t *t;
+ t = e->trans;
+ if (!e->processed && (t && !t->ignore)) {
+ fprintf(stderr, "Warning: element '%s' was not processed:\n", e->gi);
+ PrintLocation(e, stderr);
+ }
+}
+
+/* ______________________________________________________________________ */
+/* For each element find transpec.
+ * Arguments:
+ * Pointer to element under consideration.
+ */
+void
+PrepTranspecs(
+ Element_t *e
+)
+{
+ Trans_t *t;
+ t = FindTrans(e, 0);
+ e->trans = t;
+}
+
+/* ______________________________________________________________________ */
+/* Copy a buffer/string into another, expanding regular variables and immediate
+ * variables. (Special variables are done later.)
+ * Arguments:
+ * Pointer to string to expand.
+ * Pointer to expanded string. (return)
+ * Pointer to element under consideration.
+ */
+void
+ExpandVariables(
+ char *in,
+ char *out,
+ Element_t *e
+)
+{
+ register int i, j;
+ char *ip, *vp, *op;
+ char *def_val, *s, *atval, *modifier;
+ char vbuf[500];
+ int lev;
+
+ ip = in;
+ op = out;
+ while (*ip) {
+ /* start of regular variable? */
+ if (*ip == '$' && *(ip+1) == L_CURLY && *(ip+2) != '_') {
+ ip++;
+ ip++; /* point at variable name */
+ vp = vbuf;
+ /* Look for matching (closing) curly. (watch for nesting)
+ * We store the variable content in a tmp buffer, so we don't
+ * clobber the input buffer.
+ */
+ lev = 0;
+ while (*ip) {
+ if (*ip == L_CURLY) lev++;
+ if (*ip == R_CURLY) {
+ if (lev == 0) {
+ ip++;
+ break;
+ }
+ else lev--;
+ }
+ *vp++ = *ip++; /* copy to variable buffer */
+ }
+ *vp = EOS;
+ /* vbuf now contains the variable name (stuff between curlys). */
+ if (lev != 0) {
+ fprintf(stderr, "Botched variable use: %s\n", in);
+ /* copy rest of string if we can't recover ?? */
+ return;
+ }
+ /* Now, expand variable. */
+ vp = vbuf;
+
+ /* Check for immediate variables -- like _special variables but
+ * interpreted right now. These start with a "+" */
+
+ if ( *vp == '+' ) {
+
+ if ( ! strcmp(vp, "+content") ) {
+ for ( i=0; i<e->ncont; i++ ) {
+ if ( IsContData(e, i) ) {
+ j = strlen(ContData(e,i));
+ memcpy(op, ContData(e,i), j);
+ op += j;
+ } else {
+ fprintf(stderr, "warning: ${+current} skipped element content\n");
+ }
+ }
+
+ } else {
+ fprintf(stderr, "unknown immediate variable: %s\n", vp);
+ }
+
+ } else {
+
+ /* See if this variable has a default [ format: ${varname def} ] */
+
+ def_val = vp;
+ while (*def_val && *def_val != ' ') def_val++;
+ if (*def_val) *def_val++ = EOS;
+ else def_val = 0;
+ /* def_val now points to default, if it exists, null if not. */
+
+ modifier = vp;
+ while (*modifier && *modifier != ':') modifier++;
+ if (*modifier) *modifier++ = EOS;
+ else modifier = 0;
+ /* modifier now points to modifier if it exists, null if not. */
+
+ s = 0;
+ /* if attribute of current elem with this name found, use value */
+ if (e && (atval = FindAttValByName(e, vp)))
+ s = atval;
+ else /* else try for (global) variable with this name */
+ s = FindMappingVal(Variables, vp);
+
+ /* If we found a value, copy it to the output buffer. */
+
+ if (s) {
+ if ( modifier && *modifier == 'l' ) {
+ while (*s) {
+ *op = tolower(*s);
+ op++, *s++;
+ }
+ } else
+ while (*s) *op++ = *s++;
+ } else
+ if (def_val) {
+ while (*def_val) *op++ = *def_val++;
+ }
+ }
+ continue;
+ }
+ *op++ = *ip++;
+ }
+ *op = EOS; /* terminate string */
+}
+
+/* ______________________________________________________________________ */
+/* Process an "output" translation spec - one of StartText, EndText,
+ * Replace, Message. (These are the ones that produce output.)
+ * Steps done:
+ * Expand attributes and regular varaibles in input string.
+ * Pass thru string, accumulating chars to be sent to output stream.
+ * If we find the start of a special variable, output what we've
+ * accumulated, then find the special variable's "bounds" (ie, the
+ * stuff between the curly brackets), and expand that by passing to
+ * ExpandSpecialVar(). Continue until done the input string.
+ * Arguments:
+ * Input buffer (string) to be expanded and output.
+ * Pointer to element under consideration.
+ * FILE pointer to where to write output.
+ * Flag saying whether to track the character position we're on
+ * (passed to OutputString).
+ */
+void
+ProcesOutputSpec(
+ char *ib,
+ Element_t *e,
+ FILE *fp,
+ int track_pos
+)
+{
+ char obuf[LINESIZE];
+ char vbuf[LINESIZE];
+ char *dest, vname[LINESIZE], *cp;
+ int esc;
+
+ obuf[0] = EOS; /* start with empty output buffer */
+
+ ExpandVariables(ib, vbuf, e); /* expand regular variables */
+ ib = vbuf;
+ dest = obuf;
+
+ esc = 0;
+ while (*ib) {
+ /* If not a $, it's a regular char. Just copy it and go to next. */
+ if (*ib != '$') { /* look for att/variable marker */
+ *dest++ = *ib++; /* it's not. just copy character */
+ continue;
+ }
+
+ /* We have a $. What we have must be a "special variable" since
+ * regular variables have already been expanded, or just a lone $. */
+
+ if (ib[1] != L_CURLY) { /* just a stray dollar sign (no variable) */
+ *dest++ = *ib++;
+ continue;
+ }
+
+ ib++; /* point past $ */
+
+ /* Output what we have in buffer so far. */
+ *dest = EOS; /* terminate string */
+ if (obuf[0]) OutputString(obuf, fp, track_pos);
+ dest = obuf; /* ready for new stuff in buffer */
+
+ if (!strchr(ib, R_CURLY)) {
+ fprintf(stderr, "Mismatched braces in TranSpec: %s\n", ib);
+ /* how do we recover from this? */
+ }
+ ib++;
+ cp = vname;
+ while (*ib && *ib != R_CURLY) *cp++ = *ib++;
+ *cp = EOS; /* terminate att/var name */
+ ib++; /* point past closing curly */
+ /* we now have special variable name (stuff in curly {}'s) in vname */
+ ExpandSpecialVar(&vname[1], e, fp, track_pos);
+ }
+ *dest = EOS; /* terminate string in output buffer */
+
+ if (obuf[0]) OutputString(obuf, fp, track_pos);
+}
+
+/* ______________________________________________________________________ */
+/* Find the translation spec for the given tag.
+ * Returns pointer to first spec that matches (name, depth, etc., of tag).
+ * Arguments:
+ * e -- Pointer to element under consideration.
+ * specID -- name of specid that we're looking for
+ * Return:
+ * Pointer to translation spec that matches given element's context.
+ */
+
+Trans_t *
+FindTrans(
+ Element_t *e,
+ int specID
+)
+{
+ char context[LINESIZE], buf[LINESIZE], *cp, **vec, *atval;
+ int i, a, match;
+ Trans_t *t, *tt;
+
+ /* loop through all transpecs */
+ for (t=TrSpecs; t; t=t->next)
+ {
+ /* Only one of gi or gilist will be set. */
+ /* Check if elem name matches */
+ if (t->gi && !StrEq(t->gi, e->gi) && !specID) continue;
+
+ /* test if we're looking for a specific specID and then if
+ * this is it.. */
+ if (specID)
+ if (!t->my_id || (specID != t->my_id))
+ continue;
+
+ /* Match one in the list of GIs? */
+ if (t->gilist) {
+ for (match=0,vec=t->gilist; *vec; vec++) {
+ if (StrEq(*vec, e->gi)) {
+ match = 1;
+ break;
+ }
+ }
+ if (!match) continue;
+ }
+
+ /* Check context */
+
+ /* Special case of context */
+ if (t->parent)
+ if (!QRelation(e, t->parent, REL_Parent)) continue;
+
+ if (t->context) { /* no context specified -> a match */
+ FindContext(e, t->depth, context);
+
+ /* If reg expr set, do regex compare; else just string compare. */
+ if (t->context_re) {
+ if (! regexec(t->context_re, context)) continue;
+ }
+ else {
+ /* Is depth of spec deeper than element's depth? */
+ if (t->depth > e->depth) continue;
+
+ /* See if context of element matches "context" of transpec */
+ match = ( (t->context[0] == context[0]) &&
+ !strcmp(t->context, context) );
+ if (!match) continue;
+ }
+ }
+
+ /* Check attributes. Loop through list, comparing each. */
+ if (t->nattpairs) { /* no att specified -> a match */
+ for (match=1,a=0; a<t->nattpairs; a++) {
+ if (!(atval = FindAttValByName(e, t->attpair[a].name))) {
+ match = 0;
+ break;
+ }
+ if (!regexec(t->attpair[a].rex, atval)) match = 0;
+ }
+ if (!match) continue;
+ }
+
+ /* Check relationships: child, parent, ancestor, sib, ... */
+ if (t->relations) {
+ Mapping_t *r;
+ match = 1;
+ for (r=t->relations->maps,i=0; i<t->relations->n_used; i++) {
+ if (!CheckRelation(e, r[i].name, r[i].sval, 0, 0, RA_Current)) {
+ match = 0;
+ break;
+ }
+ }
+ if (!match) continue;
+ }
+
+ /* check this element's parent's attribute */
+ if (t->pattrset && e->parent) {
+ char *p, **tok;
+
+ i = 2;
+ match = 1;
+ tok = Split(t->pattrset, &i, S_STRDUP);
+ if ( i == 2 ) {
+ p = FindAttValByName(e->parent, tok[0]);
+ ExpandVariables(tok[1], buf, 0);
+ if ( !p || strcmp(p, buf) )
+ match = 0;
+ } else {
+ if (!FindAttValByName(e->parent, t->pattrset))
+ match = 0;
+ }
+ free(tok[0]);
+ if (!match) continue;
+ }
+
+ /* check this element's "birth order" */
+ if (t->nth_child) {
+ /* First one is called "1" by the user. Internally called "0". */
+ i = t->nth_child;
+ if (i > 0) { /* positive # -- count from beginning */
+ if (e->my_eorder != (i-1)) continue;
+ }
+ else { /* negative # -- count from end */
+ i = e->parent->necont - i;
+ if (e->my_eorder != i) continue;
+ }
+ }
+
+ /* check that variables match */
+ if (t->var_name) {
+ cp = FindMappingVal(Variables, t->var_name);
+ if (!cp || strcmp(cp, t->var_value)) continue;
+ }
+
+ /* check for variable regular expression match */
+ if ( t->var_RE_name ) {
+ cp = FindMappingVal(Variables, t->var_RE_name);
+ if (!cp || !regexec(t->var_RE_value, cp)) continue;
+ }
+
+ /* check content */
+ if (t->content) { /* no att specified -> a match */
+ for (match=0,i=0; i<e->ndcont; i++) {
+ if (regexec(t->content_re, e->dcont[i])) {
+ match = 1;
+ break;
+ }
+ }
+ if (!match) continue;
+ }
+
+ /* -------- at this point we've passed all criteria -------- */
+
+ /* See if we should be using another transpec's actions. */
+ if (t->use_id) {
+ if (t->use_id < 0) return &NullTrans; /* missing? */
+ /* see if we have a pointer to that transpec */
+ if (t->use_trans) return t->use_trans;
+ for (tt=TrSpecs; tt; tt=tt->next) {
+ if (t->use_id == tt->my_id) {
+ /* remember pointer for next time */
+ t->use_trans = tt;
+ return t->use_trans;
+ }
+ }
+ t->use_id = -1; /* flag it as missing */
+ fprintf(stderr, "Warning: transpec ID (%d) not found for %s.\n",
+ t->use_id, e->gi);
+ return &NullTrans;
+ }
+
+ return t;
+ }
+
+ /* At this point, we have not found a matching spec. See if there
+ * is a wildcard, and if so, use it. (Wildcard GI is named "*".) */
+ if ((t = FindTransByName("*"))) return t;
+
+ if (warnings && !specID)
+ fprintf(stderr, "Warning: transpec not found for %s\n", e->gi);
+
+ /* default spec - pass character data and descend node */
+ return &NullTrans;
+}
+
+/* ______________________________________________________________________ */
+/* Find translation spec by (GI) name. Returns the first one that matches.
+ * Arguments:
+ * Pointer to name of transpec (the "gi" field of the Trans structure).
+ * Return:
+ * Pointer to translation spec that matches name.
+ */
+
+Trans_t *
+FindTransByName(
+ char *s
+)
+{
+ Trans_t *t;
+
+ for (t=TrSpecs; t; t=t->next) {
+ /* check if tag name matches (first check 1st char, for efficiency) */
+ if (t->gi) {
+ if (*(t->gi) != *s) continue; /* check 1st character */
+ if (!strcmp(t->gi, s)) return t;
+ }
+ }
+ return NULL;
+}
+
+/* Find translation spec by its ID (SpecID).
+ * Arguments:
+ * Spec ID (an int).
+ * Return:
+ * Pointer to translation spec that matches name.
+ */
+Trans_t *
+FindTranByID(int n)
+{
+ Trans_t *t;
+
+ for (t=TrSpecs; t; t=t->next)
+ if (n == t->my_id) return t;
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* Process a "chunk" of content data of an element.
+ * Arguments:
+ * Pointer to data content to process
+ * FILE pointer to where to write output.
+ */
+
+void
+DoData(
+ char *data,
+ FILE *fp,
+ int verbatim
+)
+{
+ if (!fp) return;
+ OutputString(data, fp, 1);
+}
+
+/* ______________________________________________________________________ */
+/* Handle a processing instruction. This is done similarly to elements,
+ * where we find a transpec, then do what it says. Differences: PI names
+ * start with '_' in the spec file (if a GI does not start with '_', it
+ * may be forced to upper case, sgmls keeps PIs as mixed case); the args
+ * to the PI are treated as the data of an element. Note that a PI wildcard
+ * is "_*"
+ * Arguments:
+ * Pointer to the PI.
+ * FILE pointer to where to write output.
+ */
+
+void
+DoPI(
+ char *pi,
+ FILE *fp
+)
+{
+ char buf[250], **tok;
+ int n;
+ Trans_t *t;
+
+ buf[0] = '_';
+ strcpy(&buf[1], pi);
+ n = 2;
+ tok = Split(buf, &n, 0);
+ if ((t = FindTransByName(tok[0])) ||
+ (t = FindTransByName("_*"))) {
+ if (t->replace) ProcesOutputSpec(t->replace, 0, fp, 1);
+ else {
+ if (t->starttext) ProcesOutputSpec(t->starttext, 0, fp, 1);
+ if (t->ignore != IGN_DATA) /* skip data nodes? */
+ if (n > 1) OutputString(tok[1], fp, 1);
+ if (t->endtext) ProcesOutputSpec(t->endtext, 0, fp, 1);
+ }
+ if (t->message) ProcesOutputSpec(t->message, 0, stderr, 0);
+ }
+ else {
+ /* If not found, just print the PI in square brackets, along
+ * with a warning message. */
+ fprintf(fp, "[%s]", pi);
+ if (warnings) fprintf(stderr, "Warning: Unrecognized PI: [%s]\n", pi);
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Set and increment variables, as appropriate, if the transpec says to.
+ * Arguments:
+ * Pointer to translation spec for current element.
+ */
+
+static void
+set_and_increment(
+ Trans_t *t,
+ Element_t *e
+)
+{
+ Mapping_t *m;
+ int i, inc, n;
+ char *cp, buf[50];
+ char ebuf[500];
+
+ /* set/reset variables */
+ if (t->set_var) {
+ for (m=t->set_var->maps,i=0; i<t->set_var->n_used; i++) {
+ ExpandVariables(m[i].sval, ebuf, e); /* do some expansion */
+ SetMappingNV(Variables, m[i].name, ebuf);
+ }
+ }
+
+ /* increment counters */
+ if (t->incr_var) {
+ for (m=t->incr_var->maps,i=0; i<t->incr_var->n_used; i++) {
+ cp = FindMappingVal(Variables, m[i].name);
+ /* if not set at all, set to 1 */
+ if (!cp) SetMappingNV(Variables, m[i].name, "1");
+ else {
+ if (isdigit(*cp) || (*cp == '-' && isdigit(cp[1]))) {
+ n = atoi(cp);
+ if (m[i].sval && isdigit(*m[i].sval)) inc = atoi(m[i].sval);
+ else inc = 1;
+ sprintf(buf, "%d", (n + inc));
+ SetMappingNV(Variables, m[i].name, buf);
+ } else
+ if (!*(cp+1) && isalpha(*cp)) {
+ buf[0] = *cp + 1;
+ buf[1] = 0;
+ SetMappingNV(Variables, m[i].name, buf);
+ }
+ }
+ }
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Translate one element.
+ * Arguments:
+ * Pointer to element under consideration.
+ * FILE pointer to where to write output.
+ * Pointer to translation spec for current element, or null.
+ */
+void
+TransElement(
+ Element_t *e,
+ FILE *fp,
+ Trans_t *t
+)
+{
+ int i;
+
+ if (!t) t = ((e && e->trans) ? e->trans : &NullTrans);
+
+ /* see if we should quit. */
+ if (t->quit) {
+ fprintf(stderr, "Quitting at location:\n");
+ PrintLocation(e, fp);
+ fprintf(stderr, "%s\n", t->quit);
+ exit(1);
+ }
+
+ /* See if we want to replace subtree (do text, don't descend subtree) */
+ if (t->replace) {
+ ProcesOutputSpec(t->replace, e, fp, 1);
+ if (t->message) ProcesOutputSpec(t->message, e, stderr, 0);
+ set_and_increment(t, e); /* adjust variables, if appropriate */
+ return;
+ }
+
+ if (t->starttext) ProcesOutputSpec(t->starttext, e, fp, 1);
+ if (t->message) ProcesOutputSpec(t->message, e, stderr, 0);
+
+ /* Process data for this node and descend child elements/nodes. */
+ if (t->ignore != IGN_ALL) {
+ /* Is there a "generated" node at the front of this one? */
+ if (e->gen_trans[0]) {
+ Trans_t *tp;
+ if ((tp = FindTranByID(e->gen_trans[0]))) {
+ if (tp->starttext) ProcesOutputSpec(tp->starttext, e, fp, 1);
+ if (tp->message) ProcesOutputSpec(tp->message, e, stderr, 0);
+ if (tp->endtext) ProcesOutputSpec(tp->endtext, e, fp, 1);
+ }
+ }
+ /* Loop thruthe "nodes", whether data, child element, or PI. */
+ for (i=0; i<e->ncont; i++) {
+ if (IsContElem(e,i)) {
+ if (t->ignore != IGN_CHILDREN) /* skip child nodes? */
+ TransElement(ContElem(e,i), fp, NULL);
+ }
+ else if (IsContData(e,i)) {
+ if (t->ignore != IGN_DATA) /* skip data nodes? */
+ DoData(ContData(e,i), fp, t->verbatim);
+ }
+ else if (IsContPI(e,i))
+ DoPI(e->cont[i].ch.data, fp);
+ }
+ /* Is there a "generated" node at the end of this one? */
+ if (e->gen_trans[1]) {
+ Trans_t *tp;
+ if ((tp = FindTranByID(e->gen_trans[1]))) {
+ if (tp->starttext) ProcesOutputSpec(tp->starttext, e, fp, 1);
+ if (tp->message) ProcesOutputSpec(tp->message, e, stderr, 0);
+ if (tp->endtext) ProcesOutputSpec(tp->endtext, e, fp, 1);
+ }
+ }
+ }
+
+ set_and_increment(t, e); /* adjust variables, if appropriate */
+
+ if (t->endtext) ProcesOutputSpec(t->endtext, e, fp, 1);
+
+ e->processed = 1;
+}
+
+/* ______________________________________________________________________ */
+/* Check if element matches specified relationship, and, if it does, perform
+ * action on either current element or matching element (depends on flag).
+ * Arguments:
+ * Pointer to element under consideration.
+ * Pointer to relationship name.
+ * Pointer to related element name (GI).
+ * Pointer to action to take (string - turned into an int).
+ * FILE pointer to where to write output.
+ * Flag saying whether to do action on related element (RA_Related)
+ * or on current element (RA_Current).
+ * Return:
+ * Bool, saying whether (1) or not (0) relationship matches.
+ */
+
+int
+CheckRelation(
+ Element_t *e,
+ char *relname, /* relationship name */
+ char *related, /* related element */
+ char *actname, /* action to take */
+ FILE *fp,
+ RelAction_t flag
+)
+{
+ Element_t *ep;
+ Relation_t r;
+
+ if ((r = FindRelByName(relname)) == REL_Unknown) return 0;
+ if (!(ep=QRelation(e, related, r))) return 0;
+
+ if (!actname) return 1; /* no action - return what we found */
+
+ switch (flag) {
+ case RA_Related: TranTByAction(ep, actname, fp); break;
+ case RA_Current: TranTByAction(e, actname, fp); break;
+ }
+ return 1;
+}
+
+/* ______________________________________________________________________ */
+/* Perform action given by a SpecID on the given element.
+ * Arguments:
+ * Pointer to element under consideration.
+ * SpecID of action to perform.
+ * FILE pointer to where to write output.
+ *
+ */
+void
+TranByAction(
+ Element_t *e,
+ int n,
+ FILE *fp
+)
+{
+ Trans_t *t;
+
+ t = FindTranByID(n);
+ if (!t) {
+ fprintf(stderr, "Could not find named action for %d.\n", n);
+ return;
+ }
+ TransElement(e, fp, t);
+}
+
+/* ______________________________________________________________________ */
+/* Perhaps perform action given by a SpecID on the given element.
+ * Arguments:
+ * Pointer to element under consideration.
+ * SpecID of action to perform. Unlike TranByAction, this is the argument
+ * as it occurred in the transpec (ASCII) and may end with the letter
+ * "t" which means that the transpec mustpass criteria selection.
+ * FILE pointer to where to write output.
+ */
+void
+TranTByAction(
+ Element_t *e,
+ char *strn,
+ FILE *fp
+)
+{
+ int n;
+ Trans_t *t;
+
+ n = atoi(strn);
+ if ( strn[strlen(strn)-1] != 't' ) {
+ t = FindTranByID(n);
+ if (!t) {
+ fprintf(stderr, "Could not find named action for %d.\n", n);
+ return;
+ }
+ } else {
+ t = FindTrans(e, n);
+ if ( !t || !t->my_id )
+ return;
+ }
+ TransElement(e, fp, t);
+}
+
+/* ______________________________________________________________________ */
diff --git a/usr.bin/sgmls/instant/translate.h b/usr.bin/sgmls/instant/translate.h
new file mode 100644
index 0000000..777d591
--- /dev/null
+++ b/usr.bin/sgmls/instant/translate.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 1994
+ * Open Software Foundation, Inc.
+ *
+ * Permission is hereby granted to use, copy, modify and freely distribute
+ * the software in this file and its documentation for any purpose without
+ * fee, provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation. Further, provided that the name of Open
+ * Software Foundation, Inc. ("OSF") not be used in advertising or
+ * publicity pertaining to distribution of the software without prior
+ * written permission from OSF. OSF makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Copyright (c) 1996 X Consortium
+ * Copyright (c) 1995, 1996 Dalrymple Consulting
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the X Consortium and
+ * Dalrymple Consulting shall not be used in advertising or otherwise to
+ * promote the sale, use or other dealings in this Software without prior
+ * written authorization.
+ */
+/* ________________________________________________________________________
+ *
+ * Program to manipulate SGML instances.
+ *
+ * These are data definitions for the "translating" portion of the program.
+ *
+ * ________________________________________________________________________
+ */
+
+#ifdef STORAGE
+#ifndef lint
+static char *tr_h_RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/translate.h,v 1.3 1996/06/02 21:47:32 fld Exp $";
+#endif
+#endif
+
+#define L_CURLY '{'
+#define R_CURLY '}'
+
+/* things to ignore when processing an element */
+#define IGN_NONE 0
+#define IGN_ALL 1
+#define IGN_DATA 2
+#define IGN_CHILDREN 3
+
+/* for CheckRelation() */
+typedef enum { RA_Current, RA_Related } RelAction_t;
+
+typedef struct {
+ char *name; /* attribute name string */
+ char *val; /* attribute value string */
+ regexp *rex; /* attribute value reg expr (compiled) */
+} AttPair_t;
+
+typedef struct _Trans {
+ /* criteria */
+ char *gi; /* element name of tag under consideration */
+ char **gilist; /* list of element names (multiple gi's) */
+ char *context; /* context in tree - looking depth levels up */
+ regexp *context_re; /* tree heirarchy looking depth levels up */
+ int depth; /* number of levels to look up the tree */
+ AttPair_t *attpair; /* attr name-value pairs */
+ int nattpairs; /* number of name-value pairs */
+ char *parent; /* GI has this element as parent */
+ int nth_child; /* GI is Nth child of this of parent element */
+ char *content; /* element has this string in content */
+ regexp *content_re; /* content reg expr (compiled) */
+ char *pattrset; /* is this attr set (any value) in parent? */
+ char *var_name; /* variable name */
+ char *var_value; /* variable value */
+ char *var_RE_name; /* variable name (for VarREValue) */
+ regexp *var_RE_value; /* variable value (compiled, for VarREValue) */
+ Map_t *relations; /* various relations to check */
+
+ /* actions */
+ char *starttext; /* string to output at the start tag */
+ char *endtext; /* string to output at the end tag */
+ char *replace; /* string to replace this subtree with */
+ char *message; /* message for stderr, if element encountered */
+ int ignore; /* flag - ignore content or data of element? */
+ int verbatim; /* flag - pass content verbatim or do cmap? */
+ char *var_reset;
+ char *increment; /* increment these variables */
+ Map_t *set_var; /* set these variables */
+ Map_t *incr_var; /* increment these variables */
+ char *quit; /* print message and exit */
+
+ /* pointers and bookkeeping */
+ int my_id; /* unique (hopefully) ID of this transpec */
+ int use_id; /* use transpec whose ID is this */
+ struct _Trans *use_trans; /* pointer to other transpec */
+ struct _Trans *next; /* linked list */
+ int lineno; /* line number of end of transpec */
+} Trans_t;
+
+#ifdef def
+#undef def
+#endif
+#ifdef STORAGE
+# define def
+#else
+# define def extern
+#endif
+
+def Trans_t *TrSpecs;
+def Mapping_t *CharMap;
+def int nCharMap;
+
+/* prototypes for things defined in translate.c */
+int CheckRelation(Element_t *, char *, char *, char *, FILE*, RelAction_t);
+Trans_t *FindTrans(Element_t *, int);
+Trans_t *FindTransByName(char *);
+Trans_t *FindTransByID(int);
+void PrepTranspecs(Element_t *);
+void ProcessOneSpec(char *, Element_t *, FILE *, int);
+void TransElement(Element_t *, FILE *, Trans_t *);
+void TranByAction(Element_t *, int, FILE *);
+void TranTByAction(Element_t *, char *, FILE *);
+
+/* prototypes for things defined in tranvar.c */
+void ExpandSpecialVar(char *, Element_t *, FILE *, int);
+
+/* prototypes for things defined in tables.c */
+void OSFtable(Element_t *, FILE *, char **, int);
+
+/* ______________________________________________________________________ */
+
diff --git a/usr.bin/sgmls/instant/transpec.5 b/usr.bin/sgmls/instant/transpec.5
new file mode 100644
index 0000000..297b9d0
--- /dev/null
+++ b/usr.bin/sgmls/instant/transpec.5
@@ -0,0 +1,528 @@
+...\"
+...\"
+...\" Copyright (c) 1994
+...\" Open Software Foundation, Inc.
+...\"
+...\" Permission is hereby granted to use, copy, modify and freely distribute
+...\" the software in this file and its documentation for any purpose without
+...\" fee, provided that the above copyright notice appears in all copies and
+...\" that both the copyright notice and this permission notice appear in
+...\" supporting documentation. Further, provided that the name of Open
+...\" Software Foundation, Inc. ("OSF") not be used in advertising or
+...\" publicity pertaining to distribution of the software without prior
+...\" written permission from OSF. OSF makes no representations about the
+...\" suitability of this software for any purpose. It is provided "as is"
+...\" without express or implied warranty.
+...\"
+...\" Copyright (c) 1996 X Consortium
+...\" Copyright (c) 1996 Dalrymple Consulting
+...\"
+...\" Permission is hereby granted, free of charge, to any person obtaining a copy
+...\" of this software and associated documentation files (the "Software"), to deal
+...\" in the Software without restriction, including without limitation the rights
+...\" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+...\" copies of the Software, and to permit persons to whom the Software is
+...\" furnished to do so, subject to the following conditions:
+...\"
+...\" The above copyright notice and this permission notice shall be included in
+...\" all copies or substantial portions of the Software.
+...\"
+...\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+...\" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+...\" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+...\" X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+...\" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+...\" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+...\" OTHER DEALINGS IN THE SOFTWARE.
+...\"
+...\" Except as contained in this notice, the names of the X Consortium and
+...\" Dalrymple Consulting shall not be used in advertising or otherwise to
+...\" promote the sale, use or other dealings in this Software without prior
+...\" written authorization.
+...\"
+...\" Translated with /usr/local/lib/tpt/ref-man.ts by fld on cord, Wed 07 Feb 1996, 22:00
+.TH "\fBtranspec\fP" "file format"
+.SH "Name"
+\fBtranspec\fP - translation specification for \fBinstant\fP
+.SH "Synopsis"
+.na
+.PP
+\fBfile.ts\fP
+.ad
+.SH "Description"
+.PP
+The \fBtranspec\fP file is used by the \fBinstant\fP program to translate an SGML document instance to a format suitable for a formatting application.
+The convention is to name the file with the suffix \fB.ts\fP.
+.PP
+A \fBtranspec\fP file is composed of a number of individual translation specs.
+Each translation spec (transpec) is made up of a number of fields, one per line.
+Translation specs are separated by a line with a leading dash. Text after the dash is ignored.
+Fields are composed of two parts, a name and a value, separated by a colon.
+The colon must immediately follow the name, and any amount of whitespace (blanks and tabs) may be present between the colon and value.
+Values should not be quoted, and you should be careful of trailing spaces.
+(Trailing space will be considered part of the value.)
+Quotes, if they appear, will be considered part of the value of the fields.
+Lines that begin with whitespace (blanks and tabs) are a continuation of the previous line; the leading space is ignored.
+These characteristics are very similar to those of e-mail headers.
+Lines beginning with a \fB#\fP (number sign) are comments and blank lines are ignored.
+.SH "Field Descriptions"
+.PP
+Some fields are for identifying criteria that determines if a particular spec matches an element in the instance.
+Others specify what action is to take place when a match happens, such as sending text to the output stream.
+.SS "Criteria fields"
+.PP
+Criteria fields restrict the conditions under which a single translation spec will apply.
+If each field specified in a particular transpec matches an element under consideration in the document instance,
+then that translation spec is said to match. The appropriate actions, as specified in that spec, are then taken.
+The program, \fBinstant\fP, searches the list of transpecs in the order given in the file.
+Therefore, the more restrictive specs (those with more criteria) should appear before less restrictive ones.
+.PP
+For quick reference, this is a brief summary of the possible criteria fields for translation. A complete discussion of each follows.
+.P
+.TS
+tab(@);
+l l l.
+\fBField Label\fR@\fBField Value\fR@\fBDescription\fR
+GI@gi ...@name of this GI
+AttValue@attname reg-expr@current element has attribute with value
+Content@reg-expr@is reg-expr in char content>
+Context@context@element context, up the tree
+NthChild@number@current element is Nth child of its parent
+PAttSet@attname (val)@parent has this attribute set (optional to value val)
+Relation@relationship gi@gi has relationship to current element
+VarREValue@var REvalue@variable is set to regular expression value
+VarValue@var value@variable is set to value
+.TE
+'br\" labeled list
+.IP "\fBGI:\fP \fIgi\fP [...]"
+\fIgi\fP is the name of the generic identifier, or element name, to consider.
+More than one GI may appear in this field.
+.IP "\fBAttValue:\fP \fIattname\fP \fIregular-expression\fP"
+This is an attribute name-value pair, where \fIattname\fP is an attribute if the GI.
+The \fIregular-expression\fP is of the form accepted by the unix program \fBegrep\fP.
+This pair is compared to the corresponding attribute name-value pairs of the GI under consideration.
+To simply test if an attribute us set, use \fB.\fP (a dot) for \fIregular-expression\fP.
+There may be more than one of these lines for each transpec.
+.IP "\fBContent:\fP \fIregular-expression\fP"
+This specifies that the character content of GI contains a string matching the \fIregular-expression\fP.
+.IP "\fBContext:\fP \fIcontext\fP"
+This specifies the \fIcontext\fP in which to apply this translation spec.
+It is either a list of generic identifiers or a regular expression describing a list of generic identifiers, looking up the hierarchy.
+The first is the parent of the GI.
+.IP "\fBNthChild:\fP \fInumber\fP"
+This specifies that the GI is the \fInumber\fPth child element of its parent.
+Children are numbered starting with \fB1\fP.
+Negative numbers may be used to indicate order counting backwards.
+For example, -1 denotes the last child.
+.IP "\fBPAttSet:\fP \fIattname\fP"
+This specifies that the parent has this attribute, \fIattname\fP, set to any value (not IMPLIED). A value to match may optionally
+be specified after attname.
+.IP "\fBRelation:\fP \fIrelationship\fP \fIgi\fP"
+This specifies that the current element has the \fIrelationship\fP to the named \fIgi\fP.
+The acceptable relationships are: \fBancestor\fP (anywhere up the tree), \fBchild\fP (immediate child),
+\fBdescendant\fP (anywhere down the tree), \fBparent\fP (immediate ancestor), \fBsibling\fP (share same parent element),
+\fBsibling+\fP (any later sibling), \fBsibling+1\fP (the immediately following sibling), \fBsibling-\fP (any earlier sibling),
+\fBsibling-1\fP (the immediately following sibling).
+.IP "\fBVarREValue:\fP \fIvarname\fP \fIREvalue\fP"
+This specifies that the global variable \fIvarname\fP has the value \fIREvalue\fP,
+where \fIREvalue\fP is a regular expression
+(see the \fBVarValue\fP statement).
+.IP "\fBVarValue:\fP \fIvarname\fP \fIvalue\fP"
+This specifies that the global variable \fIvarname\fP has the (literal)
+value \fIvalue\fP (see the \fBVarREValue\fP statement).
+'br\" labeled list end
+.PP
+There are two special GIs.
+If specified, \fB_Start\fP and \fB_End\fP are processed as if they were GIs in the instance at the start and end of the translation, respectively.
+Their criteria are never checked. Only their actions are performed.
+.SS "Action fields"
+.PP
+For quick reference, this is a brief summary of the action fields for translation. They are only performed if all the criteria are satisfied.
+A complete discussion of each follows.
+.P
+.TS
+tab(@);
+l l l.
+\fBField Label\fR@\fBField Value\fR@\fBDescription\fR
+Action@spec-id@use transpec whose spec ID is `spec-id'
+EndText@text@text for end of element
+Increment@name@increment variable `name'
+Ignore@key@flag for ignoring element's children and/or data
+Message@text@text to send to stderr
+Quit@text@print text and quit program
+Replace@text@replace this subtree with text
+Set@name value@set variable \fIname\fP to \fIvalue\fP
+SpecID@spec-id@unique Spec ID (int) of this spec
+StartText@text@text for start of element
+.TE
+'br\" labeled list
+.IP "\fBAction:\fP \fIspec-id\fP"
+Use the actions of the spec identified by the \fBSpecID\fP with matching identifier \fIspec-id\fP.
+.IP "\fBEndText:\fP \fItext\fP"
+This specifies text to be output when the end tag is processed.
+.IP "\fBIgnore:\fP \fIkey\fP"
+This specifies that the data or children for this element are to be ignored.
+Set \fIkey\fP to \fBall\fP to ignore the element (data and child elements),
+to \fBdata\fP to ignore the immediate character data content (child elements are still descended into),
+and to \fBchildren\fP to process the immediate character data content but not descended into child elements.
+Other actions specified in this transpec are still performed, however.
+.IP "\fBIncrement:\fP \fIname\fP"
+This is used to increment a variable whose value is a number.
+If the variable is not a number, no action will be taken.
+The variable must have been previously defined. This action is done immediately before \fBEndText\fP.
+There may be more than one of these lines for each transpec.
+.IP "\fBMessage:\fP \fItext\fP"
+This specifies a string to be printed to the standard error when the matching element is processed.
+It is intended for informing the user of the progress of the translation.
+It is also used for validation of instances (see the \fB-v\fP flag of \fBinstant\fP(1));
+a spec would be written to recognize a construct that is not allowed.
+This action is done immediately after \fBStartText\fP.
+Messages are also useful for debugging spec files; one is able to easily tell when a matching spec is processed,
+without looking at the actual output of the translation.
+Note that the spec writer is responsible for putting newlines (\fB\en\fP) in the message text.
+.IP "\fBReplace:\fP \fItext\fP"
+This specifies text to replace the current subtree with.
+This is equivalent to \fBStartText\fP and \fBIgnore\fP.
+.IP "\fBQuit:\fP \fItext\fP"
+This specifies text to be printed to the standard error. The program then terminates with exit status 1.
+This is intended for bailing out when an undesirable instance is encountered
+(such as when it is known that the formatting application can never handle a class of components, like tables).
+.IP "\fBSet:\fP \fIname\fP \fIvalue\fP"
+This is used to set a variable whose name is \fIname\fP and value is \fIvalue\fP.
+Names that would be valid for GIs in the document instance are valid for variable names.
+\fIvalue\fP is the rest of the line and may be any string. This action is done immediately before \fBEndText\fP.
+There may be more than one of these lines for each transpec.
+See the discussion on variables below.
+.IP "\fBSpecID:\fP \fIspec-id\fP"
+This names the spec with the number \fIspec-id\fP. Other specs may refer to this one by this number by an \fBAction\fP field or an \fB_action\fP special variable.
+This is used for cases where several specs to perform the exact same action.
+.IP "\fBStartText:\fP \fItext\fP"
+This specifies text to be output when the start tag is processed.
+'br\" labeled list end
+.SS "Other Fields"
+.PP
+These fields may appear anywhere. The action occurs when the translation spec file is read, before any elements are translated.
+Theses are independent of any element processing.
+'br\" labeled list
+.IP "\fBVar:\fP \fIname\fP \fIvalue\fP"
+This is used to define a variable whose name is \fIname\fP and value is \fIvalue\fP.
+It is similar to \fBSet\fP, but it may occur anywhere in the file and takes effect when the spec file is read.
+'br\" labeled list end
+.SS "Text Strings"
+.PP
+The \fItext\fP referred to in the \fBStartText\fP, \fBEndText\fP, \fBReplace\fP,
+and \fBMessage\fP actions is more than simple character strings.
+Special sequences allow more complex output.
+.PP
+One type of special sequence is for C-style string processing.
+Most special characters are escaped with a \e (backslash). Like in C or shell programs, to print a \e (backslash), you must escape it with another backslash. These special character strings are:
+'br\" labeled list
+.IP "\fB\en (backslash-n)\fP"
+This specifies that a newline character is to be printed to the output stream.
+.IP "\fB\er (backslash-r)\fP"
+This specifies that a carriage return character is to be printed to the output stream.
+.IP "\fB\et (backslash-t)\fP"
+This specifies that a tab character is to be printed to the output stream.
+.IP "\fB\es (backslash-s)\fP"
+This specifies that a space is to be printed to the output stream.
+This is useful for the end of a transpec line, where it can be difficult to tell if a blank is present at the end.
+.IP "\fB\e007 (backslash-007)\fP"
+This specifies that the character whose octal value is 007 is to be printed to the output stream.
+This works for any octal character value.
+.IP "\fB^ (caret)\fP"
+This specifies the that position in the string will be at the start of a line in the output stream.
+'br\" labeled list end
+.PP
+If the first token of the text string is \fB#include\fP, then the second token is taken to be a file name and that file is included.
+If the file is not found, the library directory, as mentioned above, is searched.
+If the text string starts with a \fB!\fP (exclamation point), the rest of the line is taken to be a command and the output of that command is inserted.
+.PP
+An element's attributes may also be used in the text of output fields.
+To use an attribute value, precede its name with a \fB${\fP (dollar sign-left curly bracket) and follow it with a \fB}\fP (right curly bracket).
+(This style is followed by the Bourne shell.) For example, \fB${TYPE}\fP.
+If the attribute is not set (not IMPLIED), nothing will be printed to the output stream.
+To specify a value to use if the attribute is not set, place the value after the attribute name, separated by a space.
+To return the attribute value in lower-case, add a colon followed by
+lower-case l (\fB${TYPE:l}\fP.
+.SH "Variables"
+.PP
+Variables in \fBinstant\fP are similar to those in many other string-oriented programming languages, such as \fBsh\fP and \fBawk\fP.
+They are set by: \fBVar:\fP \fIname\fP \fIvalue\fP and \fBSet:\fP \fIname\fP \fIvalue\fP.
+Values may be set and reset to any string.
+In a \fBVar\fP line, if the value begins with a \fB!\fP,
+then the rest of the line is executed as a command, and its output is taken as the \fIvalue\fP.
+.PP
+A reference to the value of a variable follows the same syntax as
+a reference to the value of an attribute: \fB${\fIname\fB}\fR.
+If that variable has not been defined, a null value will be returned.
+A default value can be returned instead of null for an undefined variable
+by using the form: \fB${\fIname default\fB}\fR.
+.PP
+Variables may be used as attributes are, that is in any of the text strings mentioned above.
+In fact, if an attribute name is referred to and it is not set for a given element,
+\fBinstant\fP looks for a variable with the same name. This way global defaults can be set.
+If you want to be sure that you are accessing a local variable value, not an attribute value, you can use lower or mixed case names.
+Attribute names, as passed by \fBsgmls\fP, are in upper case.
+.PP
+Any number of \fBVar\fP actions may appear in the spec file. These set the values of the variables before any translation takes place.
+The \fBSet\fP actions within transpecs are performed when that spec is processed when an element matches the given criteria.
+.SS "Preset Variables"
+.PP
+Several variables are preset by \fBinstant\fP upon start of the program.
+Their values may be overridden in transpec files or on the command line.
+'br\" labeled list
+.IP "\fBdate\fP"
+This is the date and time that the program started. The format is: \f(CWTue 10 Aug 1993, 16:52\fP.
+.IP "\fBhost\fP"
+This is the name of the host where the program is run. It is what is returned by the \fBgethostname\fP library call.
+.IP "\fBtranspec\fP"
+This is the translation spec filename.
+.IP "\fBuser\fP"
+This is the login name of the user running the program.
+'br\" labeled list end
+.SS "Special Variables"
+.PP
+There is a collection of special variables called \fIspecial variables\fP.
+These are identified by starting the names with a \fB_\fP (underscore).
+This is a summary of the special variables. A complete discussion of each special variable follows.
+\fBspec-id\fP refers to a number specified in a \fBSpecID\fP field.
+When used in a special variable, it means to perform the action in that translation spec.
+.PP
+Note that when a \fIspec-id\fR is given in a special variable,
+the default is to perform the translation spec named by the \fIspec-id\fR ignoring
+of any criteria statements found there.
+For most special variables that use a \fIspec-id\fP, postpending a "\fBt\fR" to
+the \fIspec-id\fR (with no spaces between them, eg,
+"\fB${_followrel child TITLE 15t}\fR"), will cause the criteria statements
+in the named translation spec to evaluate successfully before that translation
+spec will be processed.
+.P
+.TS
+tab(@);
+l l.
+\fBVariable Usage\fR@\fBDescription\fR
+\fB_action\fP \fIspec-id\fP@do spec with id spec-id
+\fB_allatts\fP@print all attribute/value pairs
+\fB_attval\fP \fIatt\fP [\fIvalue\fP] \fIspec-id\fP@use spec-id if attribute matches
+\fB_chasetogi\fP \fIgi\fP \fIspec-id\fP@follow IDREFs until gi found
+\fB_eachatt\fP \fIatt\fP \fIspec-id\fP [\fIspec-id\fP]@do spec-id for each word of attribute value
+\fB_eachcon\fP \fIspec-id\fP [\fIspec-id\fP]@do spec-id for each word of content
+\fB_env\fP \fIenv-variable\fP@return value of env variable
+\fB_filename\fP@filename of notation
+\fB_find\fP \fIrel\fP \fIgi\fP \fIspec-id\fP@find gi based on relationship
+\fB_followlink\fP [\fIattname\fP] \fIspec-id\fP@follow IDREFs [attname] and use spec-id
+\fB_followrel\fP \fIrel\fP \fIgi\fP \fIspec-id\fP [\fIspec-id\fP]@do spec-id on rel if it matches
+\fB_gi\fP [\fBM|L|U\fP]@return GI name; M, L, U case
+\fB_id\fP \fIid [\fP\fIspec-id\fP]@find element with ID and use spec-id
+\fB_include\fP \fIfilename\fP@insert file here
+\fB_infile\fP [\fBline\fP]@instance filename [and line number]
+\fB_insertnode\fP S|E \fIspec-id\fP@do spec-id when element is traversed
+\fB_isset\fP \fIvar\fP [\fIvalue\fP] \fIspec-id\fP@do spec-id if variable matches
+\fB_location\fP@print location of current element
+\fB_namelist\fP \fIspec-id\fP [\fIspec-id\fP]@content is namelist, do spec-id for each
+\fB_nchild\fP [\fIgi\fP]@number of child elements [named \fIattname\fP]
+\fB_osftable\fP \fIformat\fP [\fIflag\fP]@print table format specification
+\fB_path\fP@print path to current element
+\fB_pattr\fP \fIattname\fP@value of parent's attribute
+\fB_pfind\fP \fIargs ...\fP@same as \fB_find\fP, but start at parent
+\fB_relation\fP \fIrel\fP \fIgi\fP \fIspec-id\fP [\fIspec-id\fP]@do spec-id if relation matches
+\fB_set\fP \fIvar\fP \fIvalue\fP@set variable to value
+\fB_!\fP\fIcommand\fP@command to run
+.TE
+'br\" labeled list
+.IP "\fB_action\fP \fIspec-id\fP"
+Use the actions of the spec identified by the \fBSpecID\fP with matching identifier \fIspec-id\fP.
+This behaves similarly to the \fBAction\fP action, but is in addition to the present translation spec.
+.IP "\fB_allatts\fP"
+Print all attribute name-value pairs of the current element to the output stream.
+The name and value are separated by a \fB=\fP (equals sign), and the value is surrounded by quotes.
+This can be useful for creating a normalized version of the instance.
+.IP "\fB_attval\fP \fIattname\fP [\fIvalue\fP] \fIspec-id\fP"
+If the current element has an attribute named \fIattname\fP, optionally whose value matches \fIvalue\fP,
+use the actions of the transpec identified by \fIspec-id\fP.
+.IP "\fB_chasetogi\fP \fIgi\fP \fIspec-id\fP"
+Follow IDREF attributes until if finds an element whose GI is \fIgi\fP or which has a child element with that GI.
+It will apply the transpec \fIspec-id\fP to that element.
+By default, \fBinstant\fP assumes the attributes named \fBLINKEND\fP, \fBLINKENDS\fP, and \fBIDREF\fP are of type IDREF or IDREFS.
+(This corresponds with the OSF DTDs.)
+You can change this by setting the variable \fBlink_atts\fP to a space-separated list of attribute names.
+.IP "\fB_eachatt\fP \fIatt\fP \fIspec-id\fP [\fIspec-id2\fP]"
+The transpec named by \fIspec-id\fR is invoked once per each word
+found in the value of the attribute \fIatt\fR.
+Inside the target transpec, the current word being processed
+is available in the variable named \fBeach_A\fR (\fB${each_A}\fR).
+If \fIspec-id2\fP is specified, it will use \fIspec-id\fP for the first word
+in the attribute and \fIspec-id2\fP for the others.
+.IP "\fB_eachcon\fP \fIspec-id\fP [\fIspec-id2\fP]"
+The transpec named by \fIspec-id\fR is invoked once per each word
+found in the content of the current element.
+Inside the target transpec, the current word being processed
+is available in the variable named \fBeach_C\fR (\fB${each_C}\fR).
+If \fIspec-id2\fP is specified, it will use \fIspec-id\fP for the first word
+in the content and \fIspec-id2\fP for the others.
+.IP "\fB_env\fP \fIenv-variable\fP"
+Print the value of the environment variable \fIenv-variable\fP to the output stream.
+.IP "\fB_filename\fP"
+Print the filename of the notation associated with this element, if any.
+This is used to get the filename of an external notation entity reference.
+For example, to print the filename in the latex macro from the epsf macro package, use \f(CW\e\eepsfboxi{${_filename}}\fP.
+.IP "\fB_find\fP [\fBtop\fP] \fIrelationship\fP \fIargs ...\fP \fIspec-id\fP"
+Descend the document hierarchy finding elements that match one of several criteria.
+When one is found, the action specified by \fIspec-id\fP is performed.
+If \fBtop\fP is specified, the search starts at the top of the document hierarchy, rather than at the current element.
+The possible values for \fIrelationship\fP are \fBgi\fP, \fBgi-parent\fP, \fBparent\fP, and \fBattr\fP,
+and take different arguments.
+Explanations may be best done by example:
+\fB_find gi CHAPTER 123\fP means to find elements whose GI is CHAPTER, and perform action 123;
+\fB_find gi-parent TITLE CHAPTER 124\fP means to find elements whose GI is TITLE and whose parent is CHAPTER, and perform action 124;
+\fB_find parent BODY 125\fP means to find elements whose parent's GI is BODY, and perform action 125;
+\fB_find attr TYPE UGLY 125\fP means to find elements whose attribute named TYPE is set to UGLY, and perform action 126.
+.IP "\fB_followlink\fP [\fIattname\fP] \fIspec-id\fP"
+When processing an element, \fBinstant\fP will follow the IDREF attributes until an element with no IDREF attributes is found.
+It will then apply the transpec specified by \fIspec-id\fP to that element.
+If specified, it will follow the link pointed to by \fIattname\fP.
+By default, \fBinstant\fP assumes the attributes named \fBLINKEND\fP and \fBLINKENDS\fP are if type IDREF or IDREFS.
+You can change this by setting the variable \fBlink_atts\fP to a space-separated list of attribute names.
+.IP "\fB_followrel\fP \fIrelationship\fP \fIgi\fP \fIspec-id\fP [\fIspec-id2\fP]"
+If the \fIgi\fP has the specified \fIrelationship\fP to the current element,
+perform the action specified by \fIspec-id\fP on the related element.
+If the \fIrelationship\fP to \fIgi\fP does not exist,
+and \fIspec-id2\fP is present, perform \fIspec-id2\fP on the current element.
+See the discussion of the criteria field \fBRelation\fP for acceptable relationship names.
+.IP "\fB_gi\fP [\fBM|L|U\fP]"
+Print the name of the current GI to the output stream.
+If specified, \fBM\fP, \fBL\fP, or \fBU\fP will ensure the GI name is printed in mixed, lower, or upper case, respectively.
+.IP "\fB_id\fP \fIid\fP [\fIspec-id\fP]"
+Find the element with \fIid\fP and use \fIspec-id\fP, if set. If not set, use the spec for that element's context.
+.IP "\fB_include\fP \fIfilename\fP"
+Insert the file \fIfilename\fP into the output stream.
+.IP "\fB_infile\fP [\fBline\fP]"
+Print the name of the sgml instance file to the output stream. If \fBline\fP is specified, also print the line number.
+This depends on \fBsgmls\fP being called with the \fB-l\fP option.
+.IP "\fB_insertnode\fP \fBS\fP|\fBE\fP \fIspec-id\fP"
+Do \fIspec-id\fP when the current element is traversed at a later pass.
+This can be considered inserting a node, without content, into the hierarchy.
+This is only useful if done to elements \fIbefore\fP they are processed.
+Typically \fB_chasetogi\fP or \fB_followlink\fP is specified early in an instance's processing,
+so that when the elements found by one of these actions are processed in their turn, the added actions are performed.
+\fB_insertnode\fP would be specified as the action of a \fIspec-id\fP pointed to in a \fB_chasetogi\fP or \fB_followlink\fP usage.
+.IP "\fB_location\fP"
+The location of the current element is printed to the output stream in several ways: the path to the element (see \fB_path\fP),
+a position hint, which is the nearest title, the line number, if the ESIS (output from \fBsgmls\fP) contains line numbers,
+and the ID of the element, if it has one.
+This is especially useful when using the \fBMessage\fP action to validate an instance.
+.IP "\fB_namelist\fP \fIspec-id\fP [\fIspec-id2\fP]"
+This assumes that the content of the current element is a namelist (a list of element IDs),
+and applies the action based on \fIspec-id\fP for each element pointed to.
+If \fIspec-id2\fP is specified, it will use \fIspec-id\fP for the first ID in the namelist and \fIspec-id2\fP for the others.
+.IP "\fB_nchild\fP [\fIgi\fP]"
+Print the number of child elements of the element to the output stream.
+If \fIgi\fP is specified, print the number of child element with that name.
+.IP "\fB_osftable\fP \fBtex\fP|\fBtbl\fP|\fBcheck\fP [\fIflag\fP]"
+Print table markup into the output stream. The format depends on whether \fBtex\fP or \fBtbl\fP is specified.
+The \fIflag\fP may be one of \fBcellstart\fP, \fBcellend\fP, \fBrowstart\fP, \fBrowend\fP, \fBtop\fP, or \fBbottom\fP.
+The value determines what markup or text will be generated.
+If \fBcellstart\fP is specified, the correct markup for the beginning of a cell is output.
+If \fBtop\fP, \fBbottom\fP, or \fBrowend\fP are specified,
+the correct markup for the end of the appropriate position is printed to the output stream.
+If \fBcheck\fP is specified, the attributes and child elements are checked for errors and consistency.
+.IP "\fB_path\fP"
+Print the path to current GI to the output stream. A path is each element, going down the tree from the topmost element.
+A number in parentheses after each element name shows which child element the next one is in the order of children for that element.
+Ordering starts at 0.
+For example: \f(CWOSF-BOOK(3) BODY(0) CHAPTER(4) SECTION\fP.
+This says the path is \fB<OSF-BOOK>\fP's third child, \fB<BODY>\fP's zeroth,
+and \fB<CHAPTER>\fP's fourth, which is named \fB<SECTION>\fP.
+.IP "\fB_pattr\fP \fIname\fP"
+Print the value of parent's attribute whose name is \fIname\fP to the output stream.
+.IP "\fB_pfind\fP \fIrel\fP \fIgi\fP \fIspec-id\fP"
+This is exactly the same as \fB_find\fP except that the search starts at the current element's parent.
+.IP "\fB_relation\fP \fIrelationship\fP \fIgi\fP \fIspec-id\fP [\fIspec-id2\fP]"
+If the \fIgi\fP has the specified \fIrelationship\fP to the current element,
+perform the action specified by \fIspec-id\fP on the current element.
+If the relationship test fails and \fIspec-id2\fP is specified, perform that action.
+See the discussion of the criteria field \fBRelation\fP for acceptable relationship names.
+.IP "\fB_set\fP \fIvarname\fP \fIvalue\fP"
+Set the value of the variable \fIvarname\fP to \fIvalue\fP.
+.IP "\fB_isset\fP \fIvarname\fP [\fIvalue\fP] \fIspec-id\fP"
+If the value of the variable \fIvarname\fP is set to \fIvalue\fP, then perform action referred to by \fIspec-id\fP.
+If \fIvalue\fP is not specified, action will be performed if \fIvarname\fP is set to any value.
+.IP "\fB_!\fP \fIcommand\fP"
+Run the command \fIcommand\fP, directing its standard output into the output stream.
+'br\" labeled list end
+.SS "Immediate Variables"
+.PP
+\fIImmediate variables\fR are like special variables, except that they
+are expanded when the transpec is originally processed (special
+variables are processed later, near when the final output is being generated).
+The general syntax of immediate variables is \fB${+\fIimmediate_variable\ ...\fB}\fR.
+.PP
+There is currently only one immediate variable defined:
+.IP "\fB+content\fP"
+This special variable is replaced by the data content of the current element.
+.SH "Examples"
+.PP
+The following will output the given string for elements whose generic identifier is \fBP\fP (for paragraph).
+At the start of processing this element, the program ensures that the output starts on a new line,
+the \fBtroff\fP macro \fB<.P>\fP is output, then a newline.
+At the end of this element processing, the program ensures that the output starts on a new line.
+.DS
+.nf
+.ft CW
+GI: P
+StartText: ^.P^
+EndText: ^
+-
+.ft R
+.fi
+.DE
+.PP
+The following will output the given string for elements whose generic identifier is \fBCMD-ARGUMENT\fP and which have an
+attribute \fBPRESENCE\fP set to the value \fBOPTIONAL\fP.
+.DS
+.nf
+.ft CW
+GI: CMD-ARGUMENT
+AttValue: PRESENCE OPTIONAL
+StartText: $\e\e[
+EndText: $\e\e]
+-
+.ft R
+.fi
+.DE
+.PP
+The following prints the section number, title, and page number of the target of a cross reference.
+Assume the cross reference points to a section element, which contains a title element.
+The criteria for this spec to match is that the attribute \fBOSFROLE\fP is set to the value \fBgetfull\fP.
+The action is to replace the content of the \fB<XREF>\fP element with the given string.
+When processing the string, \fBinstant\fP will follow the IDREF attributes of \fB<XREF>\fP
+until an element with no IDREF attributes is found. It will then apply the transpec numbered \fB87\fP to that element,
+which will print the name of the GI in mixed case into the output stream.
+It will then print the LaTeX reference instruction with the value of the \fBLINKEND\fP attribute as an argument.
+(This will become the section number after processing by LaTeX.)
+It will then follow IDREFs until if finds an element whose GI is \fBTITLE\fP or which has a child element with that GI.
+It will apply the transpec numbered \fB1\fP to that element, which copies the title into the output stream where the cross reference occurs.
+Finally, it will print the word \fBpage\fP followed by the LaTeX instruction to obtain the page number of a reference.
+.DS
+.nf
+.ft CW
+GI: XREF
+AttValue: OSFROLE getfull
+Replace: ${_followlink 87} \e\eref{${LINKEND}},\es
+ {\e\ebf ${_chasetogi TITLE 1}}, page \e\epageref{${LINKEND}}
+-
+# Print GI name, in mixed case
+GI: _pr_gi_name
+SpecID: 87
+Ignore: 1
+EndText: ${_gi M}
+-
+GI: _pass-text
+SpecID: 1
+-
+.ft R
+.fi
+.DE
+.SH "Related Information"
+.PP
+\fBinstant\fP(1), \fBsgmls\fP(1), \fBegrep\fP(1).
diff --git a/usr.bin/sgmls/instant/tranvar.c b/usr.bin/sgmls/instant/tranvar.c
new file mode 100644
index 0000000..ab04425
--- /dev/null
+++ b/usr.bin/sgmls/instant/tranvar.c
@@ -0,0 +1,763 @@
+/*
+ * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 1994
+ * Open Software Foundation, Inc.
+ *
+ * Permission is hereby granted to use, copy, modify and freely distribute
+ * the software in this file and its documentation for any purpose without
+ * fee, provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation. Further, provided that the name of Open
+ * Software Foundation, Inc. ("OSF") not be used in advertising or
+ * publicity pertaining to distribution of the software without prior
+ * written permission from OSF. OSF makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Copyright (c) 1996 X Consortium
+ * Copyright (c) 1995, 1996 Dalrymple Consulting
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the X Consortium and
+ * Dalrymple Consulting shall not be used in advertising or otherwise to
+ * promote the sale, use or other dealings in this Software without prior
+ * written authorization.
+ */
+/* ________________________________________________________________________
+ *
+ * instant - a program to manipulate SGML instances.
+ *
+ * This module is for handling "special variables". These act a lot like
+ * procedure calls
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/tranvar.c,v 1.5 1996/06/11 22:43:15 fld Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include <regexp.h>
+#include "general.h"
+#include "translate.h"
+
+static char **idrefs; /* list of IDREF att names to follow */
+static char *def_idrefs[] = { "LINKEND", "LINKENDS", "IDREF", 0 };
+static char *each_A = 0; /* last seen _eachatt */
+static char *each_C = 0; /* last seen _eachcon */
+
+/* forward references */
+void ChaseIDRefs(Element_t *, char *, char *, FILE *);
+void Find(Element_t *, int, char **, FILE *);
+void GetIDREFnames();
+
+/* ______________________________________________________________________ */
+/* Handle "special" variable - read file, run command, do action, etc.
+ * Arguments:
+ * Name of special variable to expand.
+ * Pointer to element under consideration.
+ * FILE pointer to where to write output.
+ * Flag saying whether to track the character position we're on
+ * (passed to OutputString).
+ */
+
+void
+ExpandSpecialVar(
+ char *name,
+ Element_t *e,
+ FILE *fp,
+ int track_pos
+)
+{
+ FILE *infile;
+ char buf[LINESIZE], *cp, *atval;
+ char **tok;
+ int ntok, n, i, actioni;
+ char *action, *action1;
+ Element_t *ep;
+ Trans_t *t, *tt;
+
+ /* Run a command.
+ * Format: _! command args ... */
+ if (*name == '!') {
+ name++;
+ if ((infile = popen(name, "r"))) {
+ while (fgets(buf, LINESIZE, infile)) fputs(buf, fp);
+ pclose(infile);
+ fflush(fp);
+ }
+ else {
+ fprintf(stderr, "Could not start program '%s': %s",
+ name, strerror(errno));
+ }
+ return;
+ }
+
+ /* See if caller wants one of the tokens from _eachatt or _eachcon.
+ * If so, output it and return. (Yes, I admit that this is a hack.)
+ */
+ if (*name == 'A' && name[1] == EOS && each_A) {
+ OutputString(each_A, fp, track_pos);
+ return;
+ }
+ if (*name == 'C' && name[1] == EOS && each_C) {
+ OutputString(each_C, fp, track_pos);
+ return;
+ }
+
+ ntok = 0;
+ tok = Split(name, &ntok, 0);
+
+ /* Include another file.
+ * Format: _include filename */
+ if (StrEq(tok[0], "include")) {
+ name = tok[1];
+ if (ntok > 1 ) {
+ if ((infile=OpenFile(name)) == NULL) {
+ sprintf(buf, "Can not open included file '%s'", name);
+ perror(buf);
+ return;
+ }
+ while (fgets(buf, LINESIZE, infile)) fputs(buf, fp);
+ fclose(infile);
+ }
+ else fprintf(stderr, "No file name specified for include\n");
+ return;
+ }
+
+ /* Print location (nearest title, line no, path).
+ * Format: _location */
+ else if (StrEq(tok[0], "location")) {
+ PrintLocation(e, fp);
+ }
+
+ /* Print path to this element.
+ * Format: _path */
+ else if (StrEq(tok[0], "path")) {
+ (void)FindElementPath(e, buf);
+ OutputString(buf, fp, track_pos);
+ }
+
+ /* Print name of this element (gi).
+ * Format: _gi [M|L|U] */
+ else if (StrEq(tok[0], "gi")) {
+ strcpy(buf, e->gi);
+ if (ntok >= 2) {
+ if (*tok[1] == 'L' || *tok[1] == 'l' ||
+ *tok[1] == 'M' || *tok[1] == 'm') {
+ for (cp=buf; *cp; cp++)
+ if (isupper(*cp)) *cp = tolower(*cp);
+ }
+ if (*tok[1] == 'M' || *tok[1] == 'm')
+ if (islower(buf[0])) buf[0] = toupper(buf[0]);
+ }
+ OutputString(buf, fp, track_pos);
+ }
+
+ /* Print filename of this element's associated external entity.
+ * Format: _filename */
+ else if (StrEq(tok[0], "filename")) {
+ if (!e->entity) {
+ fprintf(stderr, "Expected ext entity (internal error? bug?):\n");
+ PrintLocation(e, stderr);
+ return;
+ }
+ if (!e->entity->fname) {
+ fprintf(stderr, "Expected filename (internal error? bug?):\n");
+ PrintLocation(e, stderr);
+ return;
+ }
+ OutputString(e->entity->fname, fp, track_pos);
+ }
+
+ /* Value of parent's attribute, by attr name.
+ * Format: _pattr attname */
+ else if (StrEq(tok[0], "pattr")) {
+ ep = e->parent;
+ if (!ep) {
+ fprintf(stderr, "Element does not have a parent:\n");
+ PrintLocation(ep, stderr);
+ return;
+ }
+ if ((atval = FindAttValByName(ep, tok[1]))) {
+ OutputString(atval, fp, track_pos);
+ }
+ }
+
+ /* Use an action, given transpec's SID.
+ * Format: _action action */
+ else if (StrEq(tok[0], "action")) {
+ TranTByAction(e, tok[1], fp);
+ }
+
+ /* Number of child elements of this element.
+ * Format: _nchild */
+ else if (StrEq(tok[0], "nchild")) {
+ if (ntok > 1) {
+ for (n=0,i=0; i<e->necont; i++)
+ if (StrEq(e->econt[i]->gi, tok[1])) n++;
+ }
+ else n = e->necont;
+ sprintf(buf, "%d", n);
+ OutputString(buf, fp, track_pos);
+ }
+
+ /* number of 1st child's child elements (grandchildren from first child).
+ * Format: _n1gchild */
+ else if (StrEq(tok[0], "n1gchild")) {
+ if (e->necont) {
+ sprintf(buf, "%d", e->econt[0]->necont);
+ OutputString(buf, fp, track_pos);
+ }
+ }
+
+ /* Chase this element's pointers until we hit the named GI.
+ * Do the action if it matches.
+ * Format: _chasetogi gi action */
+ else if (StrEq(tok[0], "chasetogi")) {
+ if (ntok < 3) {
+ fprintf(stderr, "Error: Not enough args for _chasetogi.\n");
+ return;
+ }
+ actioni = atoi(tok[2]);
+ if (actioni) ChaseIDRefs(e, tok[1], tok[2], fp);
+ }
+
+ /* Follow link to element pointed to, then do action.
+ * Format: _followlink [attname] action. */
+ else if (StrEq(tok[0], "followlink")) {
+ char **s;
+ if (ntok > 2) {
+ if ((atval = FindAttValByName(e, tok[1]))) {
+ if ((ep = FindElemByID(atval))) {
+ TranTByAction(ep, tok[2], fp);
+ return;
+ }
+ }
+ else fprintf(stderr, "Error: Did not find attr: %s.\n", tok[1]);
+ return;
+ }
+ GetIDREFnames();
+ for (s=idrefs; *s; s++) {
+ /* is this IDREF attr set? */
+ if ((atval = FindAttValByName(e, *s))) {
+ ntok = 0;
+ tok = Split(atval, &ntok, S_STRDUP);
+ /* we'll follow the first one... */
+ if ((ep = FindElemByID(tok[0]))) {
+ TranTByAction(ep, tok[1], fp);
+ return;
+ }
+ else fprintf(stderr, "Error: Can not find elem for ID: %s.\n",
+ tok[0]);
+ }
+ }
+ fprintf(stderr, "Error: Element does not have IDREF attribute set:\n");
+ PrintLocation(e, stderr);
+ return;
+ }
+
+ /* Starting at this element, decend tree (in-order), finding GI.
+ * Do the action if it matches.
+ * Format: _find args ... */
+ else if (StrEq(tok[0], "find")) {
+ Find(e, ntok, tok, fp);
+ }
+
+ /* Starting at this element's parent, decend tree (in-order), finding GI.
+ * Do the action if it matches.
+ * Format: _pfind args ... */
+ else if (StrEq(tok[0], "pfind")) {
+ Find(e->parent ? e->parent : e, ntok, tok, fp);
+ }
+
+ /* Content is supposed to be a list of IDREFs. Follow each, doing action.
+ * If 2 actions are specified, use 1st for the 1st ID, 2nd for the rest.
+ * Format: _namelist action [action2] */
+ else if (StrEq(tok[0], "namelist")) {
+ int id;
+ action1 = tok[1];
+ if (ntok > 2) action = tok[2];
+ else action = action1;
+ for (i=0; i<e->ndcont; i++) {
+ n = 0;
+ tok = Split(e->dcont[i], &n, S_STRDUP);
+ for (id=0; id<n; id++) {
+ if (fold_case)
+ for (cp=tok[id]; *cp; cp++)
+ if (islower(*cp)) *cp = toupper(*cp);
+ if ((e = FindElemByID(tok[id]))) {
+ if (id) TranTByAction(e, action, fp);
+ else TranTByAction(e, action1, fp); /* first one */
+ }
+ else fprintf(stderr, "Error: Can not find ID: %s.\n", tok[id]);
+ }
+ }
+ }
+
+ /* For each word in the element's content, do action.
+ * Format: _eachcon action [action] */
+ else if (StrEq(tok[0], "eachcon")) {
+ int id;
+ action1 = tok[1];
+ if (ntok > 3) action = tok[2];
+ else action = action1;
+ for (i=0; i<e->ndcont; i++) {
+ n = 0;
+ tok = Split(e->dcont[i], &n, S_STRDUP|S_ALVEC);
+ for (id=0; id<n; id++) {
+ each_C = tok[id];
+ TranTByAction(e, action, fp);
+ }
+ free(*tok);
+ }
+ }
+ /* For each word in the given attribute's value, do action.
+ * Format: _eachatt attname action [action] */
+ else if (StrEq(tok[0], "eachatt")) {
+ int id;
+ action1 = tok[2];
+ if (ntok > 3) action = tok[3];
+ else action = action1;
+ if ((atval = FindAttValByName(e, tok[1]))) {
+ n = 0;
+ tok = Split(atval, &n, S_STRDUP|S_ALVEC);
+ for (id=0; id<n; id++) {
+ each_A = tok[id];
+ if (id) TranTByAction(e, action, fp);
+ else TranTByAction(e, action1, fp); /* first one */
+ }
+ free(*tok);
+ }
+ }
+
+ /* Do action on this element if element has [relationship] with gi.
+ * Format: _relation relationship gi action [action] */
+ else if (StrEq(tok[0], "relation")) {
+ if (ntok >= 4) {
+ if (!CheckRelation(e, tok[1], tok[2], tok[3], fp, RA_Current)) {
+ /* action not done, see if alt action specified */
+ if (ntok >= 5)
+ TranTByAction(e, tok[4], fp);
+ }
+ }
+ }
+
+ /* Do action on followed element if element has [relationship] with gi.
+ * If [relationship] is not met, do alternate action on this element.
+ * Format: _followrel relationship gi action [action] */
+ else if (StrEq(tok[0], "followrel")) {
+ if (ntok >= 4) {
+ if (!CheckRelation(e, tok[1], tok[2], tok[3], fp, RA_Related)) {
+ /* action not done, see if an alt action specified */
+ if (ntok >= 5)
+ TranTByAction(e, tok[4], fp);
+ }
+ }
+ }
+
+ /* Find element with matching ID and do action. If action not specified,
+ * choose the right one appropriate for its context.
+ * Format: _id id [action] */
+ else if (StrEq(tok[0], "id")) {
+ if ((ep = FindElemByID(tok[1]))) {
+ if (ntok > 2) TranTByAction(ep, tok[2], fp);
+ else {
+ t = FindTrans(ep, 0);
+ TransElement(ep, fp, t);
+ }
+ }
+ }
+
+ /* Set variable to value.
+ * Format: _set name value */
+ else if (StrEq(tok[0], "set")) {
+ SetMappingNV(Variables, tok[1], tok[2]);
+ }
+
+ /* Do action if variable is set, optionally to value.
+ * If not set, do nothing.
+ * Format: _isset varname [value] action */
+ else if (StrEq(tok[0], "isset")) {
+ if ((cp = FindMappingVal(Variables, tok[1]))) {
+ if (ntok == 3) TranTByAction(e, tok[2], fp);
+ else if (ntok > 3 && !strcmp(cp, tok[2]))
+ TranTByAction(e, tok[3], fp);
+ }
+ }
+
+ /* Insert a node into the tree at start/end, pointing to action to perform.
+ * Format: _insertnode S|E action */
+ else if (StrEq(tok[0], "insertnode")) {
+ actioni = atoi(tok[2]);
+ if (*tok[1] == 'S') e->gen_trans[0] = actioni;
+ else if (*tok[1] == 'E') e->gen_trans[1] = actioni;
+ }
+
+ /* Do an CALS DTD table spec for TeX or troff. Looks through attributes
+ * and determines what to output. "check" means to check consistency,
+ * and print error messages.
+ * This is (hopefully) the only hard-coded part of instant.
+ *
+ * This was originally written for the OSF DTDs and recoded by FLD for
+ * CALS tables (since no one will ever use the OSF tables). Although
+ * TeX was addressed first, it seems that a fresh approach was required,
+ * and so, tbl is the first to be really *fixed*. Once tbl is stable,
+ * and there is a need for TeX again, that part will be recoded.
+ *
+ * *Obsolete* form (viz, for TeX):
+ * Format: _calstable [clear|check|tex]
+ * [cellstart|cellend|rowstart|rowend|top|bottom]
+ *
+ * New, good form:
+ *
+ * Format: _calstable [tbl]
+ * [tablestart|tableend|tablegroup|tablefoot|rowstart|
+ * rowend|entrystart|entryend]
+ */
+
+ else if (StrEq(tok[0], "calstable")) {
+ CALStable(e, fp, tok, ntok);
+ }
+
+ /* Do action if element's attr is set, optionally to value.
+ * If not set, do nothing.
+ * Format: _attval att [value] action */
+ else if (StrEq(tok[0], "attval")) {
+ if ((atval = FindAttValByName(e, tok[1]))) {
+ if (ntok == 3) TranTByAction(e, tok[2], fp);
+ else if (ntok > 3 && !strcmp(atval, tok[2]))
+ TranTByAction(e, tok[3], fp);
+ }
+ }
+ /* Same thing, but look at parent */
+ else if (StrEq(tok[0], "pattval")) {
+ if ((atval = FindAttValByName(e->parent, tok[1]))) {
+ if (ntok == 3) {
+ TranTByAction(e, tok[2], fp);
+ }
+ if (ntok > 3 && !strcmp(atval, tok[2]))
+ TranTByAction(e, tok[3], fp);
+ }
+ }
+
+ /* Print each attribute and value for the current element, hopefully
+ * in a legal sgml form: <elem-name att1="value1" att2="value2:> .
+ * Format: _allatts */
+ else if (StrEq(tok[0], "allatts")) {
+ for (i=0; i<e->natts; i++) {
+ if (i != 0) putc(' ', fp);
+ fputs(e->atts[i].name, fp);
+ fputs("=\"", fp);
+ fputs(e->atts[i].sval, fp);
+ putc('"', fp);
+ }
+ }
+
+ /* Print the element's input filename, and optionally, the line number.
+ * Format: _infile [line] */
+ else if (StrEq(tok[0], "infile")) {
+ if (e->infile) {
+ if (ntok > 1 && !strcmp(tok[1], "root")) {
+ strcpy(buf, e->infile);
+ if ((cp = strrchr(buf, '.'))) *cp = EOS;
+ fputs(buf, fp);
+ }
+ else {
+ fputs(e->infile, fp);
+ if (ntok > 1 && !strcmp(tok[1], "line"))
+ fprintf(fp, " %d", e->lineno);
+ }
+ return;
+ }
+ else fputs("input-file??", fp);
+ }
+
+ /* Get value of an environement variable */
+ else if (StrEq(tok[0], "env")) {
+ if (ntok > 1 && (cp = getenv(tok[1]))) {
+ OutputString(cp, fp, track_pos);
+ }
+ }
+
+ /* If the element is not empty do specid.
+ * Format: _notempty spec-id */
+ else if (StrEq(tok[0], "notempty")) {
+ if (ntok > 1 && e->ncont) {
+ TranTByAction(e, tok[1], fp);
+ }
+ }
+
+ /* Something unknown */
+ else {
+ fprintf(stderr, "Unknown special variable: %s\n", tok[0]);
+ tt = e->trans;
+ if (tt && tt->lineno)
+ fprintf(stderr, "Used in transpec, line %d\n", tt->lineno);
+ }
+ return;
+}
+
+/* ______________________________________________________________________ */
+/* return the value for the special variables _A (last processed _eachatt)
+ * and _C (last processed _eachcon)
+ */
+
+char *
+Get_A_C_value(const char * name)
+{
+ if ( !strcmp(name, "each_A") ) {
+ if ( each_A ) {
+ return each_A;
+ } else {
+ fprintf(stderr, "Requested value for unset _A variable\n");
+ }
+ } else
+ if ( !strcmp(name, "each_C") ) {
+ if ( each_C ) {
+ return each_C;
+ } else {
+ fprintf(stderr, "Requested value for unset _C variable\n");
+ }
+ } else {
+ fprintf(stderr, "Requested value for unknown special variable '%s'\n",
+ name);
+ }
+ return "";
+}
+
+/* ______________________________________________________________________ */
+/* Chase IDs until we find an element whose GI matches. We also check
+ * child element names, not just the names of elements directly pointed
+ * at (by IDREF attributes).
+ */
+
+void
+GetIDREFnames()
+{
+ char *cp;
+
+ if (!idrefs) {
+ /* did user or transpec set the variable */
+ if ((cp = FindMappingVal(Variables, "link_atts")))
+ idrefs = Split(cp, 0, S_STRDUP|S_ALVEC);
+ else
+ idrefs = def_idrefs;
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Chase ID references - follow IDREF(s) attributes until we find
+ * a GI named 'gi', then perform given action on that GI.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Name of GI we're looking for.
+ * Spec ID of action to take.
+ * FILE pointer to where to write output.
+ */
+void
+ChaseIDRefs(
+ Element_t *e,
+ char *gi,
+ char * action,
+ FILE *fp
+)
+{
+ int ntok, i, ei;
+ char **tok, **s, *atval;
+
+ /* First, see if we got what we came for with this element */
+ if (StrEq(e->gi, gi)) {
+ TranTByAction(e, action, fp);
+ return;
+ }
+ GetIDREFnames();
+
+ /* loop for each attribute of type IDREF(s) */
+ for (s=idrefs; *s; s++) {
+ /* is this IDREF attr set? */
+ if ((atval = FindAttValByName(e, *s))) {
+ ntok = 0;
+ tok = Split(atval, &ntok, 0);
+ for (i=0; i<ntok; i++) {
+ /* get element pointed to */
+ if ((e = FindElemByID(tok[i]))) {
+ /* OK, we found a matching GI name */
+ if (StrEq(e->gi, gi)) {
+ /* process using named action */
+ TranTByAction(e, action, fp);
+ return;
+ }
+ else {
+ /* this elem itself did not match, try its children */
+ for (ei=0; ei<e->necont; ei++) {
+ if (StrEq(e->econt[ei]->gi, gi)) {
+ TranTByAction(e->econt[ei], action, fp);
+ return;
+ }
+ }
+ /* try this elem's IDREF attributes */
+ ChaseIDRefs(e, gi, action, fp);
+ return;
+ }
+ }
+ else {
+ /* should not happen, since parser checks ID/IDREFs */
+ fprintf(stderr, "Error: Could not find ID %s\n", atval);
+ return;
+ }
+ }
+ }
+ }
+ /* if the pointers didn't lead to the GI, give error */
+ if (!s)
+ fprintf(stderr, "Error: Could not find '%s'\n", gi);
+}
+
+/* ______________________________________________________________________ */
+
+/* state to pass to recursive routines - so we don't have to use
+ * global variables. */
+typedef struct {
+ char *gi;
+ char *gi2;
+ char action[10];
+ Element_t *elem;
+ FILE *fp;
+} Descent_t;
+
+static void
+tr_find_gi(
+ Element_t *e,
+ Descent_t *ds
+)
+{
+ if (StrEq(ds->gi, e->gi))
+ if (ds->action[0]) TranTByAction(e, ds->action, ds->fp);
+}
+
+static void
+tr_find_gipar(
+ Element_t *e,
+ Descent_t *ds
+)
+{
+ if (StrEq(ds->gi, e->gi) && e->parent &&
+ StrEq(ds->gi2, e->parent->gi))
+ if (ds->action[0]) TranTByAction(e, ds->action, ds->fp);
+}
+
+static void
+tr_find_attr(
+ Element_t *e,
+ Descent_t *ds
+)
+{
+ char *atval;
+ if ((atval = FindAttValByName(e, ds->gi)) && StrEq(ds->gi2, atval))
+ TranTByAction(e, ds->action, ds->fp);
+}
+
+static void
+tr_find_parent(
+ Element_t *e,
+ Descent_t *ds
+)
+{
+ if (QRelation(e, ds->gi, REL_Parent)) {
+ if (ds->action[0]) TranTByAction(e, ds->action, ds->fp);
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Descend tree, finding elements that match criteria, then perform
+ * given action.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Number of tokens in special variable.
+ * Vector of tokens in special variable (eg, "find" "gi" "TITLE")
+ * FILE pointer to where to write output.
+ */
+void
+Find(
+ Element_t *e,
+ int ac,
+ char **av,
+ FILE *fp
+)
+{
+ Descent_t DS; /* state passed to recursive routine */
+
+ memset(&DS, 0, sizeof(Descent_t));
+ DS.elem = e;
+ DS.fp = fp;
+
+ /* see if we should start at the top of instance tree */
+ if (StrEq(av[1], "top")) {
+ av++;
+ ac--;
+ e = DocTree;
+ }
+ if (ac < 4) {
+ fprintf(stderr, "Bad '_find' specification - missing args.\n");
+ return;
+ }
+ /* Find elem whose GI is av[2] */
+ if (StrEq(av[1], "gi")) {
+ DS.gi = av[2];
+ strcpy(DS.action, av[3]);
+ DescendTree(e, tr_find_gi, 0, 0, &DS);
+ }
+ /* Find elem whose GI is av[2] and whose parent GI is av[3] */
+ else if (StrEq(av[1], "gi-parent")) {
+ DS.gi = av[2];
+ DS.gi2 = av[3];
+ strcpy(DS.action, av[4]);
+ DescendTree(e, tr_find_gipar, 0, 0, &DS);
+ }
+ /* Find elem whose parent GI is av[2] */
+ else if (StrEq(av[0], "parent")) {
+ DS.gi = av[2];
+ strcpy(DS.action, av[3]);
+ DescendTree(e, tr_find_parent, 0, 0, &DS);
+ }
+ /* Find elem whose attribute av[2] has value av[3] */
+ else if (StrEq(av[0], "attr")) {
+ DS.gi = av[2];
+ DS.gi2 = av[3];
+ strcpy(DS.action, av[4]);
+ DescendTree(e, tr_find_attr, 0, 0, &DS);
+ }
+}
+
+/* ______________________________________________________________________ */
+
diff --git a/usr.bin/sgmls/instant/util.c b/usr.bin/sgmls/instant/util.c
new file mode 100644
index 0000000..eb6015d
--- /dev/null
+++ b/usr.bin/sgmls/instant/util.c
@@ -0,0 +1,1109 @@
+/*
+ * Copyright 1993 Open Software Foundation, Inc., Cambridge, Massachusetts.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 1994
+ * Open Software Foundation, Inc.
+ *
+ * Permission is hereby granted to use, copy, modify and freely distribute
+ * the software in this file and its documentation for any purpose without
+ * fee, provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation. Further, provided that the name of Open
+ * Software Foundation, Inc. ("OSF") not be used in advertising or
+ * publicity pertaining to distribution of the software without prior
+ * written permission from OSF. OSF makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+/*
+ * Copyright (c) 1996 X Consortium
+ * Copyright (c) 1995, 1996 Dalrymple Consulting
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * X CONSORTIUM OR DALRYMPLE CONSULTING BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the X Consortium and
+ * Dalrymple Consulting shall not be used in advertising or otherwise to
+ * promote the sale, use or other dealings in this Software without prior
+ * written authorization.
+ */
+/* ________________________________________________________________________
+ *
+ * General utility functions for 'instant' program. These are used
+ * throughout the rest of the program.
+ *
+ * Entry points for this module:
+ * Split(s, &n, flags) split string into n tokens
+ * NewMap(slot_incr) create a new mapping structure
+ * FindMapping(map, name) find mapping by name; return mapping
+ * FindMappingVal(map, name) find mapping by name; return value
+ * SetMapping(map, s) set mapping based on string
+ * OpenFile(filename) open file, looking in inst path
+ * FilePath(filename) find path to a file
+ * FindElementPath(elem, s) find path to element
+ * PrintLocation(ele, fp) print location of element in tree
+ * NearestOlderElem(elem, name) find prev elem up tree with name
+ * OutputString(s, fp, track_pos) output string
+ * AddElemName(name) add elem to list of known elements
+ * AddAttName(name) add att name to list of known atts
+ * FindAttByName(elem, name) find an elem's att by name
+ * FindContext(elem, lev, context) find context of elem
+ * QRelation(elem, name, rel_flag) find relation elem has to named elem
+ * DescendTree(elem, enter_f, leave_f, data_f, dp) descend doc tree,
+ * calling functions for each elem/node
+ * ________________________________________________________________________
+ */
+
+#ifndef lint
+static char *RCSid =
+ "$Header: /usr/src/docbook-to-man/Instant/RCS/util.c,v 1.4 1996/06/02 21:47:32 fld Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <regexp.h>
+/* CSS don't have it and I don't see where it's used
+#include <values.h>
+*/
+
+#include "general.h"
+#include "translate.h"
+
+/* ______________________________________________________________________ */
+/* "Split" a string into tokens. Given a string that has space-separated
+ * (space/tab) tokens, return a pointer to an array of pointers to the
+ * tokens. Like what the shell does with *argv[]. The array can be is
+ * static or allocated. Space can be allocated for string, or allocated.
+ * Arguments:
+ * Pointer to string to pick apart.
+ * Pointer to max number of tokens to find; actual number found is
+ * returned. If 0 or null pointer, use a 'sane' maximum number (hard-
+ * code). If more tokens than the number specified, make last token be
+ * a single string composed of the rest of the tokens (includes spaces).
+ * Flag. Bit 0 says whether to make a copy of input string (since we'll
+ * clobber parts of it). To free the string, use the pointer to
+ * the first token returned by the function (or *ret_value).
+ * Bit 1 says whether to allocate the vector itself. If not, use
+ * (and return) a static vector.
+ * Return:
+ * Pointer to the provided string (for convenience of caller).
+ */
+
+char **
+Split(
+ char *s, /* input string */
+ int *ntok, /* # of tokens desired (input)/found (return) */
+ int flag /* dup string? allocate a vector? */
+)
+{
+ int maxnt, i=0;
+ int n_alloc;
+ char **tokens;
+ static char *local_tokens[100];
+
+ /* Figure max number of tokens (maxnt) to find. 0 means find them all. */
+ if (ntok == NULL)
+ maxnt = 100;
+ else {
+ if (*ntok <= 0 || *ntok > 100) maxnt = 100; /* arbitrary size */
+ else maxnt = *ntok;
+ *ntok = 0;
+ }
+
+ if (!s) return 0; /* no string */
+
+ /* Point to 1st token (there may be initial space) */
+ while (*s && IsWhite(*s)) s++; /* skip initial space, if any */
+ if (*s == EOS) return 0; /* none found? */
+
+ /* See if caller wants us to copy the input string. */
+ if (flag & S_STRDUP) s = strdup(s);
+
+ /* See if caller wants us to allocate the returned vector. */
+ if (flag & S_ALVEC) {
+ n_alloc = 20;
+ Malloc(n_alloc, tokens, char *);
+ /* if caller did not specify max tokens to find, set to more than
+ * there will possibly ever be */
+ if (!ntok || !(*ntok)) maxnt = 10000;
+ }
+ else tokens = local_tokens;
+
+ i = 0; /* index into vector */
+ tokens[0] = s; /* s already points to 1st token */
+ while (i<maxnt) {
+ tokens[i] = s; /* point vector member at start of token */
+ i++;
+ /* If we allocated vector, see if we need more space. */
+ if ((flag & S_ALVEC) && i >= n_alloc) {
+ n_alloc += 20;
+ Realloc(n_alloc, tokens, char *);
+ }
+ if (i >= maxnt) break; /* is this the last one? */
+ while (*s && !IsWhite(*s)) s++; /* skip past end of token */
+ if (*s == EOS) break; /* at end of input string? */
+ if (*s) *s++ = EOS; /* terminate token string */
+ while (*s && IsWhite(*s)) s++; /* skip space - to next token */
+ }
+ if (ntok) *ntok = i; /* return number of tokens found */
+ tokens[i] = 0; /* null-terminate vector */
+ return tokens;
+}
+
+/* ______________________________________________________________________ */
+/* Mapping routines. These are used for name-value pairs, like attributes,
+ * variables, and counters. A "Map" is an opaque data structure used
+ * internally by these routines. The caller gets one when creating a new
+ * map, then hands it to other routines that need it. A "Mapping" is a
+ * name/value pair. The user has access to this.
+ * Here's some sample usage:
+ *
+ * Map *V;
+ * V = NewMap(20);
+ * SetMappingNV(V, "home", "/users/bowe");
+ * printf("Home: %s\n", FindMappingVal(V, "home");
+ */
+
+/* Allocate new map structure. Only done once for each map/variable list.
+ * Arg:
+ * Number of initial slots to allocate space for. This is also the
+ * "chunk size" - how much to allocate when we use up the given space.
+ * Return:
+ * Pointer to the (opaque) map structure. (User passes this to other
+ * mapping routines.)
+ */
+Map_t *
+NewMap(
+ int slot_increment
+)
+{
+ Map_t *M;
+ Calloc(1, M, Map_t);
+ /* should really do the memset's in Calloc/Malloc/Realloc
+ macros, but that will have to wait until time permits -CSS */
+ memset((char *)M, 0, sizeof(Map_t));
+ if (!slot_increment) slot_increment = 1;
+ M->slot_incr = slot_increment;
+ return M;
+}
+
+/* Given pointer to a Map and a name, find the mapping.
+ * Arguments:
+ * Pointer to map structure (as returned by NewMap().
+ * Variable name.
+ * Return:
+ * Pointer to the matching mapping structure, or null if not found.
+ */
+Mapping_t *
+FindMapping(
+ Map_t *M,
+ const char *name
+)
+{
+ int i;
+ Mapping_t *m;
+
+ if (!M || M->n_used == 0) return NULL;
+ for (m=M->maps,i=0; i<M->n_used; i++)
+ if (m[i].name[0] == name[0] && !strcmp(m[i].name, name)) return &m[i];
+ return NULL;
+
+}
+
+/* Given pointer to a Map and a name, return string value of the mapping.
+ * Arguments:
+ * Pointer to map structure (as returned by NewMap().
+ * Variable name.
+ * Return:
+ * Pointer to the value (string), or null if not found.
+ */
+char *
+FindMappingVal(
+ Map_t *M,
+ const char *name
+)
+{
+ Mapping_t *m;
+
+ if ( !strcmp(name, "each_A") || !strcmp(name, "each_C") ) {
+ return Get_A_C_value(name);
+ }
+
+ /*
+ if (!M || M->n_used == 0) return NULL;
+ if ((m = FindMapping(M, name))) return m->sval;
+ return NULL;
+ */
+ if (!M || M->n_used == 0) {
+ return NULL;
+ }
+ if ((m = FindMapping(M, name))) {
+ return m->sval;
+ }
+ return NULL;
+
+}
+
+/* Set a mapping/variable in Map M. Input string is a name-value pair where
+ * there is some amount of space after the name. The correct mapping is done.
+ * Arguments:
+ * Pointer to map structure (as returned by NewMap().
+ * Pointer to variable name (string).
+ * Pointer to variable value (string).
+ */
+void
+SetMappingNV(
+ Map_t *M,
+ const char *name,
+ const char *value
+)
+{
+ FILE *pp;
+ char buf[LINESIZE], *cp;
+ int i;
+ Mapping_t *m;
+
+ /* First, look to see if it's a "well-known" variable. */
+ if (!strcmp(name, "verbose")) { verbose = atoi(value); return; }
+ if (!strcmp(name, "warnings")) { warnings = atoi(value); return; }
+ if (!strcmp(name, "foldcase")) { fold_case = atoi(value); return; }
+
+ m = FindMapping(M, name); /* find existing mapping (if set) */
+
+ /* OK, we have a string mapping */
+ if (m) { /* exists - just replace value */
+ free(m->sval);
+ if (value) m->sval = strdup(value);
+ else m->sval = NULL;
+ }
+ else {
+ if (name) { /* just in case */
+ /* Need more slots for mapping structures? Allocate in clumps. */
+ if (M->n_used == 0) {
+ M->n_alloc = M->slot_incr;
+ Malloc(M->n_alloc, M->maps, Mapping_t);
+ }
+ else if (M->n_used >= M->n_alloc) {
+ M->n_alloc += M->slot_incr;
+ Realloc(M->n_alloc, M->maps, Mapping_t);
+ }
+
+ m = &M->maps[M->n_used];
+ M->n_used++;
+ m->name = strdup(name);
+ if (value) m->sval = strdup(value);
+ else m->sval = NULL;
+ }
+ }
+
+ if (value)
+ {
+ /* See if the value is a command to run. If so, run the command
+ * and replace the value with the output.
+ */
+ if (*value == '!') {
+ if ((pp = popen(value+1, "r"))) { /* run cmd, read its output */
+ i = 0;
+ cp = buf;
+ while (fgets(cp, LINESIZE-i, pp)) {
+ i += strlen(cp);
+ cp = &buf[i];
+ if (i >= LINESIZE) {
+ fprintf(stderr,
+ "Prog execution of variable '%s' too long.\n",
+ m->name);
+ break;
+ }
+ }
+ free(m->sval);
+ stripNL(buf);
+ m->sval = strdup(buf);
+ pclose(pp);
+ }
+ else {
+ sprintf(buf, "Could not start program '%s'", value+1);
+ perror(buf);
+ }
+ }
+ }
+}
+
+/* Separate name and value from input string, then pass to SetMappingNV.
+ * Arguments:
+ * Pointer to map structure (as returned by NewMap().
+ * Pointer to variable name and value (string), in form "name value".
+ */
+void
+SetMapping(
+ Map_t *M,
+ const char *s
+)
+{
+ char buf[LINESIZE];
+ char *name, *val;
+
+ if (!M) {
+ fprintf(stderr, "SetMapping: Map not initialized.\n");
+ return;
+ }
+ strcpy(buf, s);
+ name = val = buf;
+ while (*val && !IsWhite(*val)) val++; /* point past end of name */
+ if (*val) {
+ *val++ = EOS; /* terminate name */
+ while (*val && IsWhite(*val)) val++; /* point to value */
+ }
+ if (name) SetMappingNV(M, name, val);
+}
+
+/* ______________________________________________________________________ */
+/* Opens a file for reading. If not found in current directory, try
+ * lib directories (from TPT_LIB env variable, or -l option).
+ * Arguments:
+ * Filename (string).
+ * Return:
+ * FILE pointer to open file, or null if it not found or can't open.
+ */
+
+FILE *
+OpenFile(
+ char *filename
+)
+{
+ FILE *fp;
+
+ filename = FilePath(filename);
+ if ((fp=fopen(filename, "r"))) return fp;
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* Opens a file for reading. If not found in current directory, try
+ * lib directories (from TPT_LIB env variable, or -l option).
+ * Arguments:
+ * Filename (string).
+ * Return:
+ * FILE pointer to open file, or null if it not found or can't open.
+ */
+
+char *
+FilePath(
+ char *filename
+)
+{
+ FILE *fp;
+ static char buf[LINESIZE];
+ int i;
+ static char **libdirs;
+ static int nlibdirs = -1;
+
+ if ((fp=fopen(filename, "r")))
+ {
+ fclose(fp);
+ strncpy(buf, filename, LINESIZE);
+ return buf;
+ }
+
+ if (*filename == '/') return NULL; /* full path specified? */
+
+ if (nlibdirs < 0) {
+ char *cp, *s;
+ if (tpt_lib) {
+ s = strdup(tpt_lib);
+ for (cp=s; *cp; cp++) if (*cp == ':') *cp = ' ';
+ nlibdirs = 0;
+ libdirs = Split(s, &nlibdirs, S_ALVEC);
+ }
+ else nlibdirs = 0;
+ }
+ for (i=0; i<nlibdirs; i++) {
+ sprintf(buf, "%s/%s", libdirs[i], filename);
+ if ((fp=fopen(buf, "r")))
+ {
+ fclose(fp);
+ return buf;
+ }
+ }
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* This will find the path to an tag. The format is the:
+ * tag1(n1):tag2(n2):tag3
+ * where the tags are going down the tree and the numbers indicate which
+ * child (the first is numbered 1) the next tag is.
+ * Returns pointer to the string just written to (so you can use this
+ * function as a printf arg).
+ * Arguments:
+ * Pointer to element under consideration.
+ * String to write path into (provided by caller).
+ * Return:
+ * Pointer to the provided string (for convenience of caller).
+ */
+char *
+FindElementPath(
+ Element_t *e,
+ char *s
+)
+{
+ Element_t *ep;
+ int i, e_path[MAX_DEPTH];
+ char *cp;
+
+ /* Move up the tree, noting "birth order" of each element encountered */
+ for (ep=e; ep; ep=ep->parent)
+ e_path[ep->depth-1] = ep->my_eorder;
+ /* Move down the tree, printing the element names to the string. */
+ for (cp=s,i=0,ep=DocTree; i<e->depth; ep=ep->econt[e_path[i]],i++) {
+ sprintf(cp, "%s(%d) ", ep->gi, e_path[i]);
+ cp += strlen(cp);
+ }
+ sprintf(cp, "%s", e->gi);
+ return s;
+}
+
+/* ______________________________________________________________________ */
+/* Print some location info about a tag. Helps user locate error.
+ * Messages are indented 2 spaces (convention for multi-line messages).
+ * Arguments:
+ * Pointer to element under consideration.
+ * FILE pointer of where to print.
+ */
+
+void
+PrintLocation(
+ Element_t *e,
+ FILE *fp
+)
+{
+ char *s, buf[LINESIZE];
+
+ if (!e || !fp) return;
+ fprintf(fp, " Path: %s\n", FindElementPath(e, buf));
+ if ((s=NearestOlderElem(e, "TITLE")))
+ fprintf(fp, " Position hint: TITLE='%s'\n", s);
+ if (e->lineno) {
+ if (e->infile)
+ fprintf(fp, " At or near instance file: %s, line: %d\n",
+ e->infile, e->lineno);
+ else
+ fprintf(fp, " At or near instance line: %d\n", e->lineno);
+ }
+ if (e->id)
+ fprintf(fp, " ID: %s\n", e->id);
+}
+
+/* ______________________________________________________________________ */
+/* Finds the data part of the nearest "older" tag (up the tree, and
+ * preceding) whose tag name matches the argument, or "TITLE", if null.
+ * Returns a pointer to the first chunk of character data.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Name (GI) of element we'll return data from.
+ * Return:
+ * Pointer to that element's data content.
+ */
+char *
+NearestOlderElem(
+ Element_t *e,
+ char *name
+)
+{
+ int i;
+ Element_t *ep;
+
+ if (!e) return 0;
+ if (!name) name = "TITLE"; /* useful default */
+
+ for (; e->parent; e=e->parent) /* move up tree */
+ for (i=0; i<=e->my_eorder; i++) { /* check preceding sibs */
+ ep = e->parent;
+ if (!strcmp(name, ep->econt[i]->gi))
+ return ep->econt[i]->ndcont ?
+ ep->econt[i]->dcont[0] : "-empty-";
+ }
+
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* Expands escaped strings in the input buffer (things like tabs, newlines,
+ * octal characters - using C style escapes).
+ */
+
+char *ExpandString(
+ char *s
+)
+{
+ char c, *sdata, *cp, *ns;
+ int len, pos, addn;
+
+ if (!s) return s;
+
+ len = strlen(s);
+ pos = 0;
+ Malloc(len + 1, ns, char);
+ ns[pos] = EOS;
+
+ for ( ; *s; s++) {
+ c = *s;
+ cp = NULL;
+
+ /* Check for escaped characters from sgmls. */
+ if (*s == '\\') {
+ s++;
+ switch (*s) {
+ case 'n':
+ c = NL;
+ break;
+
+ case '\\':
+ c = '\\';
+ break;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ /* for octal numbers (C style) of the form \012 */
+ c = *s++ - '0';
+ if (*s >= '0' && *s <= '7') {
+ c = c * 8 + (*s++ - '0');
+ if (*s >= '0' && *s <= '7')
+ c = c * 8 + (*s - '0');
+ }
+ break;
+
+ case '|': /* SDATA */
+ s++; /* point past \| */
+ sdata = s;
+ /* find matching/closing \| */
+ cp = s;
+ while (*cp && *cp != '\\' && cp[1] != '|')
+ cp++;
+ if (!*cp)
+ break;
+
+ *cp = EOS; /* terminate sdata string */
+ cp++;
+ s = cp; /* s now points to | */
+
+ cp = LookupSDATA(sdata);
+ if (!cp)
+ cp = sdata;
+ c = 0;
+ break;
+
+ /* This shouldn't happen. */
+ default:
+ s--;
+ break;
+ }
+ }
+
+ /* Check for character re-mappings. */
+ if (nCharMap && c) {
+ int i;
+
+ for (i = 0; i < nCharMap; i++) {
+ if (c != CharMap[i].name[0])
+ continue;
+ cp = CharMap[i].sval;
+ c = 0;
+ break;
+ }
+ }
+
+ /* See if there is enough space for the data. */
+ /* XXX this should be MUCH smarter about predicting
+ how much extra memory it should allocate */
+ if (c)
+ addn = 1;
+ else
+ addn = strlen(cp);
+
+ /* If not, make some. */
+ if (addn > len - pos) {
+ len += addn - (len - pos);
+ Realloc(len + 1, ns, char);
+ }
+
+ /* Then copy the data. */
+ if (c)
+ ns[pos] = c;
+ else
+ strcpy(&ns[pos], cp);
+
+ pos += addn;
+ ns[pos] = EOS;
+ }
+ return(ns);
+}
+
+/* ______________________________________________________________________ */
+/* Expands escaped strings in the input buffer (things like tabs, newlines,
+ * octal characters - using C style escapes) and outputs buffer to specified
+ * fp. The hat/anchor character forces that position to appear at the
+ * beginning of a line. The cursor position is kept track of (optionally)
+ * so that this can be done.
+ * Arguments:
+ * Pointer to element under consideration.
+ * FILE pointer of where to print.
+ * Flag saying whether or not to keep track of our position in the output
+ * stream. (We want to when writing to a file, but not for stderr.)
+ */
+
+void
+OutputString(
+ char *s,
+ FILE *fp,
+ int track_pos
+)
+{
+ char c;
+ static int char_pos = 0; /* remembers our character position */
+ char *p;
+
+ if (!fp) return;
+ if (!s) s = "^"; /* no string - go to start of line */
+
+ for (p = s; *p; p++) {
+ c = *p;
+ /* If caller wants us to track position, see if it's an anchor
+ * (ie, align at a newline). */
+ if (track_pos) {
+ if (c == ANCHOR && (p == s || *(p + 1) == EOS)) {
+ /* If we're already at the start of a line, don't do
+ * another newline. */
+ if (char_pos != 0) c = NL;
+ else c = 0;
+ }
+ else char_pos++;
+ if (c == NL) char_pos = 0;
+ }
+ else if (c == ANCHOR && (p == s || *(p + 1) == EOS)) c = NL;
+ if (c) putc(c, fp);
+ }
+}
+
+/* ______________________________________________________________________ */
+/* Figure out value of SDATA entity.
+ * We rememeber lookup hits in a "cache" (a shorter list), and look in
+ * cache before general list. Typically there will be LOTS of entries
+ * in the general list and only a handful in the hit list. Often, if an
+ * entity is used once, it'll be used again.
+ * Arguments:
+ * Pointer to SDATA entity token in ESIS.
+ * Return:
+ * Mapped value of the SDATA entity.
+ */
+
+char *
+LookupSDATA(
+ char *s
+)
+{
+ char *v;
+ static Map_t *Hits; /* remember lookup hits */
+
+ /* If we have a hit list, check it. */
+ if (Hits) {
+ if ((v = FindMappingVal(Hits, s))) return v;
+ }
+
+ v = FindMappingVal(SDATAmap, s);
+
+ /* If mapping found, remember it, then return it. */
+ if ((v = FindMappingVal(SDATAmap, s))) {
+ if (!Hits) Hits = NewMap(IMS_sdatacache);
+ SetMappingNV(Hits, s, v);
+ return v;
+ }
+
+ fprintf(stderr, "Error: Could not find SDATA substitution '%s'.\n", s);
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* Add tag 'name' of length 'len' to list of tag names (if not there).
+ * This is a list of null-terminated strings so that we don't have to
+ * keep using the name length.
+ * Arguments:
+ * Pointer to element name (GI) to remember.
+ * Return:
+ * Pointer to the SAVED element name (GI).
+ */
+
+char *
+AddElemName(
+ char *name
+)
+{
+ int i;
+ static int n_alloc=0; /* number of slots allocated so far */
+
+ /* See if it's already in the list. */
+ for (i=0; i<nUsedElem; i++)
+ if (UsedElem[i][0] == name[0] && !strcmp(UsedElem[i], name))
+ return UsedElem[i];
+
+ /* Allocate slots in blocks of N, so we don't have to call malloc
+ * so many times. */
+ if (n_alloc == 0) {
+ n_alloc = IMS_elemnames;
+ Calloc(n_alloc, UsedElem, char *);
+ }
+ else if (nUsedElem >= n_alloc) {
+ n_alloc += IMS_elemnames;
+ Realloc(n_alloc, UsedElem, char *);
+ }
+ UsedElem[nUsedElem] = strdup(name);
+ return UsedElem[nUsedElem++];
+}
+/* ______________________________________________________________________ */
+/* Add attrib name to list of attrib names (if not there).
+ * This is a list of null-terminated strings so that we don't have to
+ * keep using the name length.
+ * Arguments:
+ * Pointer to attr name to remember.
+ * Return:
+ * Pointer to the SAVED attr name.
+ */
+
+char *
+AddAttName(
+ char *name
+)
+{
+ int i;
+ static int n_alloc=0; /* number of slots allocated so far */
+
+ /* See if it's already in the list. */
+ for (i=0; i<nUsedAtt; i++)
+ if (UsedAtt[i][0] == name[0] && !strcmp(UsedAtt[i], name))
+ return UsedAtt[i];
+
+ /* Allocate slots in blocks of N, so we don't have to call malloc
+ * so many times. */
+ if (n_alloc == 0) {
+ n_alloc = IMS_attnames;
+ Calloc(n_alloc, UsedAtt, char *);
+ }
+ else if (nUsedAtt >= n_alloc) {
+ n_alloc += IMS_attnames;
+ Realloc(n_alloc, UsedAtt, char *);
+ }
+ UsedAtt[nUsedAtt] = strdup(name);
+ return UsedAtt[nUsedAtt++];
+}
+
+/* ______________________________________________________________________ */
+/* Find an element's attribute value given element pointer and attr name.
+ * Typical use:
+ * a=FindAttByName("TYPE", t);
+ * do something with a->val;
+ * Arguments:
+ * Pointer to element under consideration.
+ * Pointer to attribute name.
+ * Return:
+ * Pointer to the value of the attribute.
+ */
+
+/*
+Mapping_t *
+FindAttByName(
+ Element_t *e,
+ char *name
+)
+{
+ int i;
+ if (!e) return NULL;
+ for (i=0; i<e->natts; i++)
+ if (e->atts[i].name[0] == name[0] && !strcmp(e->atts[i].name, name))
+ return &(e->atts[i]);
+ return NULL;
+}
+*/
+
+char *
+FindAttValByName(
+ Element_t *e,
+ char *name
+)
+{
+ int i;
+ if (!e) return NULL;
+ for (i=0; i<e->natts; i++)
+ if (e->atts[i].name[0] == name[0] && !strcmp(e->atts[i].name, name))
+ return e->atts[i].sval;
+ return NULL;
+}
+
+/* ______________________________________________________________________ */
+/* Find context of a tag, 'levels' levels up the tree.
+ * Space for string is passed by caller.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Number of levels to look up tree.
+ * String to write path into (provided by caller).
+ * Return:
+ * Pointer to the provided string (for convenience of caller).
+ */
+
+char *
+FindContext(
+ Element_t *e,
+ int levels,
+ char *con
+)
+{
+ char *s;
+ Element_t *ep;
+ int i;
+
+ if (!e) return NULL;
+ s = con;
+ *s = EOS;
+ for (i=0,ep=e->parent; ep && levels; ep=ep->parent,i++,levels--) {
+ if (i != 0) *s++ = ' ';
+ strcpy(s, ep->gi);
+ s += strlen(s);
+ }
+ return con;
+}
+
+
+/* ______________________________________________________________________ */
+/* Tests relationship (specified by argument/flag) between given element
+ * (structure pointer) and named element.
+ * Returns pointer to matching tag if found, null otherwise.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Pointer to name of elem whose relationsip we are trying to determine.
+ * Relationship we are testing.
+ * Return:
+ * Pointer to the provided string (for convenience of caller).
+ */
+
+Element_t *
+QRelation(
+ Element_t *e,
+ char *s,
+ Relation_t rel
+)
+{
+ int i;
+ Element_t *ep;
+
+ if (!e) return 0;
+
+ /* we'll call e the "given element" */
+ switch (rel)
+ {
+ case REL_Parent:
+ if (!e->parent || !e->parent->gi) return 0;
+ if (!strcmp(e->parent->gi, s)) return e->parent;
+ break;
+ case REL_Child:
+ for (i=0; i<e->necont; i++)
+ if (!strcmp(s, e->econt[i]->gi)) return e->econt[i];
+ break;
+ case REL_Ancestor:
+ if (!e->parent || !e->parent->gi) return 0;
+ for (ep=e->parent; ep; ep=ep->parent)
+ if (!strcmp(ep->gi, s)) return ep;
+ break;
+ case REL_Descendant:
+ if (e->necont == 0) return 0;
+ /* check immediate children first */
+ for (i=0; i<e->necont; i++)
+ if (!strcmp(s, e->econt[i]->gi)) return e->econt[i];
+ /* then children's children (recursively) */
+ for (i=0; i<e->necont; i++)
+ if ((ep=QRelation(e->econt[i], s, REL_Descendant)))
+ return ep;
+ break;
+ case REL_Sibling:
+ if (!e->parent) return 0;
+ ep = e->parent;
+ for (i=0; i<ep->necont; i++)
+ if (!strcmp(s, ep->econt[i]->gi) && i != e->my_eorder)
+ return ep->econt[i];
+ break;
+ case REL_Preceding:
+ if (!e->parent || e->my_eorder == 0) return 0;
+ ep = e->parent;
+ for (i=0; i<e->my_eorder; i++)
+ if (!strcmp(s, ep->econt[i]->gi)) return ep->econt[i];
+ break;
+ case REL_ImmPreceding:
+ if (!e->parent || e->my_eorder == 0) return 0;
+ ep = e->parent->econt[e->my_eorder-1];
+ if (!strcmp(s, ep->gi)) return ep;
+ break;
+ case REL_Following:
+ if (!e->parent || e->my_eorder == (e->parent->necont-1))
+ return 0; /* last? */
+ ep = e->parent;
+ for (i=(e->my_eorder+1); i<ep->necont; i++)
+ if (!strcmp(s, ep->econt[i]->gi)) return ep->econt[i];
+ break;
+ case REL_ImmFollowing:
+ if (!e->parent || e->my_eorder == (e->parent->necont-1))
+ return 0; /* last? */
+ ep = e->parent->econt[e->my_eorder+1];
+ if (!strcmp(s, ep->gi)) return ep;
+ break;
+ case REL_Cousin:
+ if (!e->parent) return 0;
+ /* Now, see if element's parent has that thing as a child. */
+ return QRelation(e->parent, s, REL_Child);
+ break;
+ case REL_None:
+ case REL_Unknown:
+ fprintf(stderr, "You can not query 'REL_None' or 'REL_Unknown'.\n");
+ break;
+ }
+ return NULL;
+}
+
+/* Given a relationship name (string), determine enum symbol for it.
+ * Arguments:
+ * Pointer to relationship name.
+ * Return:
+ * Relation_t enum.
+ */
+Relation_t
+FindRelByName(
+ char *relname
+)
+{
+ if (!strcmp(relname, "?")) {
+ fprintf(stderr, "Supported query/relationships %s\n%s.\n",
+ "child, parent, ancestor, descendant,",
+ "sibling, sibling+, sibling+1, sibling-, sibling-1");
+ return REL_None;
+ }
+ else if (StrEq(relname, "child")) return REL_Child;
+ else if (StrEq(relname, "parent")) return REL_Parent;
+ else if (StrEq(relname, "ancestor")) return REL_Ancestor;
+ else if (StrEq(relname, "descendant")) return REL_Descendant;
+ else if (StrEq(relname, "sibling")) return REL_Sibling;
+ else if (StrEq(relname, "sibling-")) return REL_Preceding;
+ else if (StrEq(relname, "sibling-1")) return REL_ImmPreceding;
+ else if (StrEq(relname, "sibling+")) return REL_Following;
+ else if (StrEq(relname, "sibling+1")) return REL_ImmFollowing;
+ else if (StrEq(relname, "cousin")) return REL_Cousin;
+ else fprintf(stderr, "Unknown relationship: %s\n", relname);
+ return REL_Unknown;
+}
+
+/* ______________________________________________________________________ */
+/* This will descend the element tree in-order. (enter_f)() is called
+ * upon entering the node. Then all children (data and child elements)
+ * are operated on, calling either DescendTree() with a pointer to
+ * the child element or (data_f)() for each non-element child node.
+ * Before leaving the node (ascending), (leave_f)() is called. enter_f
+ * and leave_f are passed a pointer to this node and data_f is passed
+ * a pointer to the data/content (which includes the data itself and
+ * type information). dp is an opaque pointer to any data the caller
+ * wants to pass.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Pointer to procedure to call when entering element.
+ * Pointer to procedure to call when leaving element.
+ * Pointer to procedure to call for each "chunk" of content data.
+ * Void data pointer, passed to the avobe 3 procedures.
+ */
+
+void
+DescendTree(
+ Element_t *e,
+ void (*enter_f)(),
+ void (*leave_f)(),
+ void (*data_f)(),
+ void *dp
+)
+{
+ int i;
+ if (enter_f) (enter_f)(e, dp);
+ for (i=0; i<e->ncont; i++) {
+ if (e->cont[i].type == CMD_OPEN)
+ DescendTree(e->cont[i].ch.elem, enter_f, leave_f, data_f, dp);
+ else
+ if (data_f) (data_f)(&e->cont[i], dp);
+ }
+ if (leave_f) (leave_f)(e, dp);
+}
+
+/* ______________________________________________________________________ */
+/* Add element, 'e', whose ID is 'idval', to a list of IDs.
+ * This makes it easier to find an element by ID later.
+ * Arguments:
+ * Pointer to element under consideration.
+ * Element's ID attribute value (a string).
+ */
+
+void
+AddID(
+ Element_t *e,
+ char *idval
+)
+{
+ static ID_t *id_last;
+
+ if (!IDList) {
+ Malloc(1, id_last, ID_t);
+ IDList = id_last;
+ }
+ else {
+ Malloc(1, id_last->next, ID_t);
+ id_last = id_last->next;
+ }
+ id_last->elem = e;
+ id_last->id = idval;
+}
+
+/* ______________________________________________________________________ */
+/* Return pointer to element who's ID is given.
+ * Arguments:
+ * Element's ID attribute value (a string).
+ * Return:
+ * Pointer to element whose ID matches.
+ */
+
+Element_t *
+FindElemByID(
+ char *idval
+)
+{
+ ID_t *id;
+ for (id=IDList; id; id=id->next)
+ if (id->id[0] == idval[0] && !strcmp(id->id, idval)) return id->elem;
+ return 0;
+}
+
+/* ______________________________________________________________________ */
+
diff --git a/usr.bin/sgmls/libsgmls/Makefile b/usr.bin/sgmls/libsgmls/Makefile
new file mode 100644
index 0000000..0d058f3
--- /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..4e25957
--- /dev/null
+++ b/usr.bin/sgmls/libsgmls/sgmls.c
@@ -0,0 +1,1032 @@
+/* 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 USE_PROTOTYPES
+#define P(parms) parms
+#else
+#define P(parms) ()
+#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 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 (
+#ifdef __BORLANDC__
+ len > 0 &&
+#endif
+ 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;
+{
+#if 0
+ for (; *pp && strcmp((*pp)->name, a->name) < 0; pp = &(*pp)->next)
+ ;
+#endif
+ 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
+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..79b2658
--- /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/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..b46e9f6
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/Makefile
@@ -0,0 +1,19 @@
+#
+# 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+= lineout.c ambig.c lextaba.c catalog.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..03bf478
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/action.h
@@ -0,0 +1,180 @@
+/* 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 */
+#define ETE_ 78 /* End 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/alloc.h b/usr.bin/sgmls/sgmls/alloc.h
new file mode 100644
index 0000000..d732178
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/alloc.h
@@ -0,0 +1,8 @@
+/* alloc.h */
+
+typedef unsigned SIZE_T;
+
+/* Like malloc and realloc, but don't return if no memory is available. */
+
+extern UNIV xmalloc P((SIZE_T));
+extern UNIV xrealloc P((UNIV, SIZE_T));
diff --git a/usr.bin/sgmls/sgmls/ambig.c b/usr.bin/sgmls/sgmls/ambig.c
new file mode 100644
index 0000000..9da02eb
--- /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..2513c98
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/appl.h
@@ -0,0 +1,31 @@
+/* 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));
+
+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/catalog.c b/usr.bin/sgmls/sgmls/catalog.c
new file mode 100644
index 0000000..28077a8
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/catalog.c
@@ -0,0 +1,925 @@
+/* Normalize public identifiers to handle ISO 8879[-:]1986 problem.
+What should happen if there's a duplicate in a single catalog entry file? */
+
+#include "config.h"
+#include "std.h"
+#include "catalog.h"
+
+#ifdef USE_PROTOTYPES
+#define P(parms) parms
+#else
+#define P(parms) ()
+#endif
+
+#include "alloc.h"
+
+#define MINIMUM_DATA_CHARS \
+"abcdefghijklmnopqrstuvwxyz\
+ABCDEFGHIJKLMNOPQRSTUVWXYZ\
+0123456789-.'()+,/:=?"
+
+#define N_DECL_TYPE 3
+#define PUBLIC_ID_MAP N_DECL_TYPE
+#define N_TABLES (N_DECL_TYPE + 1)
+
+enum literal_type {
+ NORMAL_LITERAL,
+ MINIMUM_LITERAL
+};
+
+typedef enum {
+ EOF_PARAM,
+ NAME_PARAM,
+ LITERAL_PARAM
+} PARAM_TYPE;
+
+enum catalog_error {
+ E_NAME_EXPECTED,
+ E_LITERAL_EXPECTED,
+ E_ARG_EXPECTED,
+ E_MINIMUM_DATA,
+ E_EOF_COMMENT,
+ E_EOF_LITERAL,
+ E_NUL_CHAR,
+ E_CANNOT_OPEN,
+ E_GETC,
+ E_FCLOSE
+};
+
+#define FIRST_SYSTEM_ERROR E_CANNOT_OPEN
+
+#define HASH_TABLE_INITIAL_SIZE 8
+#define HASH_TABLE_MAX_SIZE (((SIZE_T)-1)/sizeof(struct hash_table_entry *))
+
+struct hash_table_entry {
+ int file_index;
+ const char *key;
+ const char *system_id;
+};
+
+/* Number of bytes per string block. */
+#define BLOCK_SIZE 1000
+
+/* Bytes follow the struct. */
+
+struct string_block {
+ struct string_block *next;
+};
+
+struct hash_table {
+ struct hash_table_entry **v;
+ SIZE_T size; /* must be power of 2 */
+ SIZE_T used;
+ SIZE_T used_limit;
+};
+
+struct catalog {
+ struct hash_table tables[N_TABLES];
+ char **files;
+ int n_files;
+ struct string_block *blocks;
+ char *block_ptr;
+ SIZE_T block_spare;
+ CATALOG_ERROR_HANDLER error_handler;
+ int loaded;
+};
+
+struct parser {
+ FILE *fp;
+ struct catalog *cat;
+ char *param;
+ SIZE_T param_length;
+ SIZE_T param_alloc;
+ int file_index;
+ const char *filename;
+ unsigned long newline_count;
+ char minimum_data[256];
+};
+
+static
+VOID add_catalog_file P((struct catalog *cat, const char *filename,
+ SIZE_T length));
+static
+VOID load P((struct catalog *cat));
+static
+VOID parse_file P((struct parser *parser));
+static
+VOID parse_public P((struct parser *parser));
+static
+VOID parse_name_map P((struct parser *parser,
+ int decl_type));
+static
+int parse_arg P((struct parser *parser));
+static
+PARAM_TYPE parse_param P((struct parser *parser, enum literal_type));
+static
+VOID skip_comment P((struct parser *parser));
+static
+PARAM_TYPE parse_literal P((struct parser *parser, int lit,
+ enum literal_type));
+static
+PARAM_TYPE parse_name P((struct parser *parser, int first_char));
+static
+VOID param_grow P((struct parser *parser));
+static
+const char *param_save P((struct parser *parser));
+static
+char *alloc_bytes P((struct catalog *catalog, SIZE_T n));
+static
+int param_equal P((struct parser *parser, const char *key));
+static
+int hash_table_add P((struct hash_table *table, const char *s,
+ const char *system_id, int file_index));
+static
+struct hash_table_entry *hash_table_lookup P((struct hash_table *table,
+ const char *s));
+static
+struct hash_table_entry *hash_table_lookup_subst P((struct hash_table *table,
+ const char *subst_table,
+ const char *s));
+static
+VOID hash_table_init P((struct hash_table *p));
+static
+VOID hash_table_delete P((struct hash_table *p));
+static
+SIZE_T hash_table_start_index P((struct hash_table *p, const char *s));
+static
+int subst_equal P((const char *subst_table, const char *s1, const char *s2));
+static
+VOID error P((struct parser *parser, enum catalog_error err));
+
+#define param_char(parser, c) \
+ ((((parser)->param_length < (parser)->param_alloc) \
+ || (param_grow(parser), 1)), \
+ ((parser)->param[(parser)->param_length] = (c)), \
+ ((parser)->param_length += 1))
+
+#define param_init(parser) ((parser)->param_length = 0)
+#define param_chop(parser) \
+ ((parser)->param_length = (parser)->param_length - 1)
+
+const char *catalog_error_text(error_number)
+ int error_number;
+{
+ static const char *text[] = {
+ "Name expected",
+ "Literal expected",
+ "Missing argument",
+ "Only minimum data characters allowed in a public identifier",
+ "End of file in comment",
+ "End of file in literal",
+ "Nul character is not allowed",
+ "Cannot open `%s': %s",
+ "Error reading `%s': %s",
+ "Error closing `%s': %s"
+ };
+ if (error_number >= 0 && error_number < sizeof(text)/sizeof(text[0]))
+ return text[error_number];
+ else
+ return "(invalid error number)";
+}
+
+
+CATALOG catalog_create(error_handler)
+ CATALOG_ERROR_HANDLER error_handler;
+{
+ int i;
+ struct catalog *p = (struct catalog *)xmalloc(sizeof(struct catalog));
+ p->loaded = 0;
+ p->n_files = 0;
+ p->files = 0;
+ p->error_handler = error_handler;
+ p->blocks = 0;
+ p->block_spare = 0;
+ p->block_ptr = 0;
+ for (i = 0; i < N_TABLES; i++)
+ hash_table_init(p->tables + i);
+ return (CATALOG)p;
+}
+
+VOID catalog_delete(cat)
+ CATALOG cat;
+{
+ int i;
+ struct string_block *block;
+ struct catalog *catalog = (struct catalog *)cat;
+ for (i = 0; i < 4; i++)
+ hash_table_delete(catalog->tables + i);
+ if (catalog->files)
+ free(catalog->files);
+ block = catalog->blocks;
+ while (block) {
+ struct string_block *tem = block;
+ block = block->next;
+ free((UNIV)tem);
+ }
+ catalog->blocks = 0;
+ free((UNIV)catalog);
+}
+
+VOID catalog_load_file(p, filename)
+ CATALOG p;
+ const char *filename;
+{
+ add_catalog_file((struct catalog *)p, filename, strlen(filename));
+}
+
+int catalog_lookup_entity(cat, public_id, name, decl_type, subst_table,
+ system_id, catalog_file)
+ CATALOG cat;
+ const char *public_id;
+ const char *name;
+ enum catalog_decl_type decl_type;
+ const char *subst_table;
+ const char **system_id;
+ const char **catalog_file;
+{
+ struct catalog *catalog = (struct catalog *)cat;
+ const struct hash_table_entry *entry = 0;
+ if (!catalog->loaded)
+ load(catalog);
+ if (public_id)
+ entry = hash_table_lookup(catalog->tables + PUBLIC_ID_MAP, public_id);
+ if (name
+ && decl_type >= 0
+ && decl_type < N_DECL_TYPE
+ && (!entry || entry->file_index > 0)) {
+ const struct hash_table_entry *entity_entry = 0;
+ if (!subst_table)
+ entity_entry = hash_table_lookup(catalog->tables + decl_type, name);
+ else
+ entity_entry = hash_table_lookup_subst(catalog->tables + decl_type,
+ subst_table, name);
+ if (!entry
+ || (entity_entry
+ && entity_entry->file_index < entry->file_index))
+ entry = entity_entry;
+ }
+ if (!entry)
+ return 0;
+ *system_id = entry->system_id;
+ *catalog_file = catalog->files[entry->file_index];
+ return 1;
+}
+
+static
+VOID add_catalog_file(cat, filename, length)
+ struct catalog *cat;
+ const char *filename;
+ SIZE_T length;
+{
+ char *s;
+ if (!cat->files)
+ cat->files = (char **)xmalloc(sizeof(char *));
+ else
+ cat->files
+ = (char **)xrealloc(cat->files, (cat->n_files + 1)*sizeof(char *));
+ s = alloc_bytes(cat, length + 1);
+ memcpy(s, filename, length);
+ s[length] = '\0';
+ cat->files[cat->n_files] = s;
+ cat->n_files += 1;
+}
+
+static
+VOID load(cat)
+ struct catalog *cat;
+{
+ int i;
+ const char *p;
+ struct parser parser;
+ const char *env_var;
+ int optional_file_index = cat->n_files;
+
+ cat->loaded = 1;
+ parser.param = 0;
+ parser.param_alloc = 0;
+ parser.cat = cat;
+ for (i = 0; i < 256; i++)
+ parser.minimum_data[i] = 0;
+ for (p = MINIMUM_DATA_CHARS; *p; p++)
+ parser.minimum_data[(unsigned char)*p] = 1;
+ env_var = getenv(CATALOG_FILES_ENV_VAR);
+ if (!env_var || *env_var == '\0')
+ env_var = DEFAULT_CATALOG_FILES;
+ for (;;) {
+ for (p = env_var; *p && *p != PATH_FILE_SEP; p++)
+ ;
+ if (p > env_var)
+ add_catalog_file(cat, env_var, p - env_var);
+ if (!*p)
+ break;
+ env_var = p + 1;
+ }
+ for (i = 0; i < cat->n_files; i++) {
+ parser.filename = cat->files[i];
+ parser.newline_count = 0;
+ parser.fp = fopen(cat->files[i], "r");
+ if (!parser.fp) {
+ if (i < optional_file_index)
+ error(&parser, E_CANNOT_OPEN);
+ }
+ else {
+ parser.file_index = i;
+ parse_file(&parser);
+ errno = 0;
+ if (fclose(parser.fp) < 0)
+ error(&parser, E_FCLOSE);
+ }
+ }
+ if (parser.param)
+ free(parser.param);
+}
+
+static
+VOID parse_file(parser)
+ struct parser *parser;
+{
+ int skipping = 0;
+ for (;;) {
+ PARAM_TYPE type = parse_param(parser, NORMAL_LITERAL);
+ if (type == NAME_PARAM) {
+ if (param_equal(parser, "PUBLIC"))
+ parse_public(parser);
+ else if (param_equal(parser, "ENTITY"))
+ parse_name_map(parser, CATALOG_ENTITY_DECL);
+ else if (param_equal(parser, "DOCTYPE"))
+ parse_name_map(parser, CATALOG_DOCTYPE_DECL);
+ else if (param_equal(parser, "LINKTYPE"))
+ parse_name_map(parser, CATALOG_LINKTYPE_DECL);
+ else
+ skipping = 1;
+ }
+ else if (type == EOF_PARAM)
+ break;
+ else if (!skipping) {
+ skipping = 1;
+ error(parser, E_NAME_EXPECTED);
+ }
+ }
+}
+
+static
+VOID parse_public(parser)
+ struct parser *parser;
+{
+ const char *public_id;
+
+ if (parse_param(parser, MINIMUM_LITERAL) != LITERAL_PARAM)
+ error(parser, E_LITERAL_EXPECTED);
+ public_id = param_save(parser);
+ if (!parse_arg(parser))
+ return;
+ hash_table_add(parser->cat->tables + PUBLIC_ID_MAP,
+ public_id, param_save(parser), parser->file_index);
+}
+
+static
+VOID parse_name_map(parser, decl_type)
+ struct parser *parser;
+ int decl_type;
+{
+ const char *name;
+
+ if (!parse_arg(parser))
+ return;
+ name = param_save(parser);
+ if (!parse_arg(parser))
+ return;
+ hash_table_add(parser->cat->tables + decl_type,
+ name, param_save(parser), parser->file_index);
+}
+
+static
+int parse_arg(parser)
+ struct parser *parser;
+{
+ PARAM_TYPE parm = parse_param(parser, NORMAL_LITERAL);
+ if (parm != NAME_PARAM && parm != LITERAL_PARAM) {
+ error(parser, E_ARG_EXPECTED);
+ return 0;
+ }
+ return 1;
+}
+
+static
+PARAM_TYPE parse_param(parser, lit_type)
+ struct parser *parser;
+ enum literal_type lit_type;
+{
+ for (;;) {
+ int c = getc(parser->fp);
+ switch (c) {
+ case EOF:
+ if (ferror(parser->fp))
+ error(parser, E_GETC);
+ return EOF_PARAM;
+ case '"':
+ case '\'':
+ return parse_literal(parser, c, lit_type);
+ case '\n':
+ parser->newline_count += 1;
+ break;
+ case '\t':
+ case ' ':
+ break;
+ case '\0':
+ error(parser, E_NUL_CHAR);
+ break;
+ case '-':
+ c = getc(parser->fp);
+ if (c == '-') {
+ skip_comment(parser);
+ break;
+ }
+ ungetc(c, parser->fp);
+ c = '-';
+ /* fall through */
+ default:
+ return parse_name(parser, c);
+ }
+ }
+}
+
+static
+VOID skip_comment(parser)
+ struct parser *parser;
+{
+ FILE *fp = parser->fp;
+ for (;;) {
+ int c = getc(fp);
+ if (c == '-') {
+ c = getc(fp);
+ if (c == '-')
+ return;
+ }
+ if (c == EOF) {
+ if (ferror(fp))
+ error(parser, E_GETC);
+ error(parser, E_EOF_COMMENT);
+ return;
+ }
+ if (c == '\n')
+ parser->newline_count += 1;
+ }
+}
+
+static
+PARAM_TYPE parse_literal(parser, lit, lit_type)
+ struct parser *parser;
+ int lit;
+ enum literal_type lit_type;
+{
+ enum { no, yes_begin, yes_middle } skipping = yes_begin;
+ FILE *fp = parser->fp;
+ param_init(parser);
+ for (;;) {
+ int c = getc(fp);
+ if (c == lit)
+ break;
+ switch (c) {
+ case '\0':
+ error(parser, E_NUL_CHAR);
+ break;
+ case EOF:
+ if (ferror(fp))
+ error(parser, E_GETC);
+ error(parser, E_EOF_LITERAL);
+ return LITERAL_PARAM;
+ case '\n':
+ parser->newline_count += 1;
+ /* fall through */
+ case ' ':
+ if (lit_type == MINIMUM_LITERAL) {
+ if (skipping == no) {
+ param_char(parser, ' ');
+ skipping = yes_middle;
+ }
+ }
+ else
+ param_char(parser, c);
+ break;
+ default:
+ if (lit_type == MINIMUM_LITERAL) {
+ if (!parser->minimum_data[c])
+ error(parser, E_MINIMUM_DATA);
+ else {
+ skipping = no;
+ param_char(parser, c);
+ }
+ }
+ else
+ param_char(parser, c);
+ break;
+ }
+ }
+ if (skipping == yes_middle)
+ param_chop(parser);
+ return LITERAL_PARAM;
+}
+
+static
+PARAM_TYPE parse_name(parser, first_char)
+ struct parser *parser;
+ int first_char;
+{
+ FILE *fp = parser->fp;
+ param_init(parser);
+ param_char(parser, first_char);
+ for (;;) {
+ int c = getc(fp);
+ switch (c) {
+ case '\0':
+ error(parser, E_NUL_CHAR);
+ break;
+ case EOF:
+ if (ferror(fp))
+ error(parser, E_GETC);
+ goto done;
+ case '\n':
+ parser->newline_count += 1;
+ goto done;
+ case ' ':
+ case '\t':
+ goto done;
+ case '"':
+ case '\'':
+ ungetc(c, fp);
+ goto done;
+ default:
+ param_char(parser, c);
+ }
+ }
+ done:
+ return NAME_PARAM;
+}
+
+static
+VOID param_grow(parser)
+ struct parser *parser;
+{
+ if (parser->param_alloc == 0) {
+ parser->param_alloc = 256;
+ parser->param = xmalloc(parser->param_alloc);
+ }
+ else {
+ parser->param_alloc *= 2;
+ parser->param = xrealloc(parser->param, parser->param_alloc);
+ }
+}
+
+static
+const char *param_save(parser)
+ struct parser *parser;
+{
+ char *s = alloc_bytes(parser->cat, parser->param_length + 1);
+ memcpy(s, parser->param, parser->param_length);
+ s[parser->param_length] = '\0';
+ return s;
+}
+
+static
+char *alloc_bytes(catalog, n)
+ struct catalog *catalog;
+ SIZE_T n;
+{
+ char *tem;
+ if (n > catalog->block_spare) {
+ struct string_block *block;
+ SIZE_T block_size = n > BLOCK_SIZE ? n : BLOCK_SIZE;
+ block
+ = (struct string_block *)xmalloc(sizeof(struct string_block)
+ + block_size);
+ block->next = catalog->blocks;
+ catalog->blocks = block;
+ catalog->block_ptr = (char *)(block + 1);
+ catalog->block_spare = block_size;
+ }
+ tem = catalog->block_ptr;
+ catalog->block_ptr += n;
+ catalog->block_spare -= n;
+ return tem;
+}
+
+
+/* Return 1 if the current parameter is equal to key. */
+
+static
+int param_equal(parser, key)
+ struct parser *parser;
+ const char *key;
+{
+ const char *param = parser->param;
+ SIZE_T param_length = parser->param_length;
+ for (; param_length > 0; param++, param_length--, key++) {
+ unsigned char c;
+ if (*key == '\0')
+ return 0;
+ c = *param;
+ if (islower(c))
+ c = toupper(c);
+ if (c != (unsigned char)*key)
+ return 0;
+ }
+ return *key == '\0';
+}
+
+/* Return 0 if it was a duplicate. */
+
+static
+int hash_table_add(table, s, system_id, file_index)
+ struct hash_table *table;
+ const char *s;
+ const char *system_id;
+ int file_index;
+{
+ SIZE_T i;
+ struct hash_table_entry *p;
+
+ if (table->size > 0) {
+ i = hash_table_start_index(table, s);
+ while (table->v[i] != 0) {
+ if (strcmp(table->v[i]->key, s) == 0)
+ return 0;
+ if (i == 0)
+ i = table->size;
+ i--;
+ }
+ }
+ if (table->used >= table->used_limit) {
+ SIZE_T j;
+ struct hash_table_entry **old_table = table->v;
+ SIZE_T old_size = table->size;
+ if (old_size == 0) {
+ table->size = HASH_TABLE_INITIAL_SIZE;
+ table->used_limit = table->size/2;
+ }
+ else {
+ if (old_size > HASH_TABLE_MAX_SIZE/2) {
+ if (old_size == HASH_TABLE_MAX_SIZE)
+ return 0; /* FIXME: give an error? */
+ table->size = HASH_TABLE_MAX_SIZE;
+ table->used_limit = HASH_TABLE_MAX_SIZE - 1;
+ }
+ else {
+ table->size = (old_size << 1);
+ table->used_limit = table->size/2;
+ }
+ }
+ table->v
+ = (struct hash_table_entry **)xmalloc(sizeof(struct hash_table_entry *)
+ * table->size);
+ for (j = 0; j < table->size; j++)
+ table->v[j] = 0;
+ for (j = 0; j < old_size; j++)
+ if (old_table[j]) {
+ SIZE_T k = hash_table_start_index(table, old_table[j]->key);
+ while (table->v[k] != 0) {
+ if (k == 0)
+ k = table->size;
+ k--;
+ }
+ table->v[k] = old_table[j];
+ }
+ if (old_table)
+ free((UNIV)old_table);
+ i = hash_table_start_index(table, s);
+ while (table->v[i] != 0) {
+ if (i == 0)
+ i = table->size;
+ i--;
+ }
+ }
+ p = (struct hash_table_entry *)xmalloc(sizeof(struct hash_table_entry));
+ p->key = s;
+ p->system_id = system_id;
+ p->file_index = file_index;
+ table->v[i] = p;
+ table->used += 1;
+ return 1;
+}
+
+static
+struct hash_table_entry *hash_table_lookup(table, s)
+ struct hash_table *table;
+ const char *s;
+{
+ if (table->size > 0) {
+ SIZE_T i;
+ i = hash_table_start_index(table, s);
+ while (table->v[i] != 0) {
+ if (strcmp(table->v[i]->key, s) == 0)
+ return table->v[i];
+ if (i == 0)
+ i = table->size;
+ i--;
+ }
+ }
+ return 0;
+}
+
+static
+struct hash_table_entry *hash_table_lookup_subst(table, subst_table, s)
+ struct hash_table *table;
+ const char *subst_table;
+ const char *s;
+{
+ SIZE_T i;
+ for (i = 0; i < table->size; i++) {
+ struct hash_table_entry *p = table->v[i];
+ if (p && subst_equal(subst_table, s, p->key))
+ return p;
+ }
+ return 0;
+}
+
+static
+VOID hash_table_init(p)
+ struct hash_table *p;
+{
+ p->v = 0;
+ p->size = 0;
+ p->used = 0;
+ p->used_limit = 0;
+}
+
+static
+VOID hash_table_delete(p)
+ struct hash_table *p;
+{
+ if (p->v) {
+ SIZE_T i;
+ for (i = 0; i < p->size; i++)
+ if (p->v[i])
+ free(p->v[i]);
+ free(p->v);
+ }
+}
+
+static
+SIZE_T hash_table_start_index(p, s)
+ struct hash_table *p;
+ const char *s;
+{
+ unsigned long h = 0;
+ while (*s)
+ h = (h << 5) + h + (unsigned char)*s++;
+ return (h & (p->size - 1));
+}
+
+/* s1 has already been substituted; s2 has not */
+
+static
+int subst_equal(subst_table, s1, s2)
+ const char *subst_table;
+ const char *s1;
+ const char *s2;
+{
+ for (; *s1 == subst_table[(unsigned char)*s2]; s1++, s2++)
+ if (*s1 == '\0')
+ return 1;
+ return 0;
+}
+
+static
+VOID error(parser, err)
+ struct parser *parser;
+ enum catalog_error err;
+{
+ (*parser->cat->error_handler)(parser->filename,
+ parser->newline_count + 1,
+ err,
+ (err >= FIRST_SYSTEM_ERROR
+ ? CATALOG_SYSTEM_ERROR
+ : 0),
+ (err >= FIRST_SYSTEM_ERROR
+ ? errno
+ : 0));
+}
+
+#ifdef MAIN
+
+static const char *program_name;
+
+#include "getopt.h"
+
+static VOID usage P((void));
+static VOID out_of_memory P((void));
+static VOID handle_catalog_error P((const char *filename,
+ unsigned long lineno,
+ int error_number,
+ unsigned flags,
+ int sys_errno));
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int entity_flag = 0;
+ enum catalog_decl_type entity_type = CATALOG_NO_DECL;
+ char *public_id = 0;
+ char *name = 0;
+ int exit_status;
+ int opt;
+ CATALOG catalog;
+ int i;
+ const char *file;
+ const char *system_id;
+
+ program_name = argv[0];
+
+ while ((opt = getopt(argc, argv, "edl")) != -1)
+ switch (opt) {
+ case 'e':
+ entity_flag = 1;
+ entity_type = CATALOG_ENTITY_DECL;
+ break;
+ case 'd':
+ entity_flag = 1;
+ entity_type = CATALOG_DOCTYPE_DECL;
+ break;
+ case 'l':
+ entity_flag = 1;
+ entity_type = CATALOG_LINKTYPE_DECL;
+ break;
+ case '?':
+ usage();
+ }
+ if (argc - optind < 2)
+ usage();
+ if (entity_flag)
+ name = argv[optind];
+ else
+ public_id = argv[optind];
+
+ catalog = catalog_create(handle_catalog_error);
+ for (i = optind + 1; i < argc; i++)
+ catalog_load_file(catalog, argv[i]);
+ if (catalog_lookup_entity(catalog, public_id, name, entity_type, (char *)0,
+ &system_id, &file)) {
+ exit_status = 0;
+ fprintf(stderr, "%s (%s)\n", system_id, file);
+ }
+ else {
+ fprintf(stderr, "not found\n");
+ exit_status = 1;
+ }
+ catalog_delete(catalog);
+ return exit_status;
+}
+
+static
+VOID usage()
+{
+ fprintf(stderr, "usage: %s [-e] [-d] [-l] id file ...\n",
+ program_name);
+ exit(1);
+}
+
+static
+VOID handle_catalog_error(filename, lineno, error_number, flags, sys_errno)
+ const char *filename;
+ unsigned long lineno;
+ int error_number;
+ unsigned flags;
+ int sys_errno;
+{
+ fprintf(stderr, "%s:", program_name);
+ if (flags & CATALOG_SYSTEM_ERROR) {
+ putc(' ', stderr);
+ fprintf(stderr, catalog_error_text(error_number), filename);
+ putc('\n', stderr);
+ }
+ else
+ fprintf(stderr, "%s:%lu: %s\n", filename, lineno,
+ catalog_error_text(error_number));
+ fflush(stderr);
+}
+
+UNIV xmalloc(n)
+ SIZE_T n;
+{
+ UNIV p = malloc(n);
+ if (!p)
+ out_of_memory();
+ return p;
+}
+
+UNIV xrealloc(p, n)
+ UNIV p;
+ SIZE_T n;
+{
+ p = realloc(p, n);
+ if (!p)
+ out_of_memory();
+ return p;
+}
+
+static
+VOID out_of_memory()
+{
+ fprintf(stderr, "%s: out of memory\n", program_name);
+ exit(1);
+}
+
+#endif /* MAIN */
diff --git a/usr.bin/sgmls/sgmls/catalog.h b/usr.bin/sgmls/sgmls/catalog.h
new file mode 100644
index 0000000..b9509a5
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/catalog.h
@@ -0,0 +1,45 @@
+#ifndef CATALOG_H
+#define CATALOG_H 1
+
+enum catalog_decl_type {
+ CATALOG_NO_DECL = -1,
+ CATALOG_ENTITY_DECL,
+ CATALOG_DOCTYPE_DECL,
+ CATALOG_LINKTYPE_DECL
+};
+
+#define CATALOG_SYSTEM_ERROR 1
+
+#ifdef __STDC__
+
+typedef void *CATALOG;
+typedef void (*CATALOG_ERROR_HANDLER)(const char *filename,
+ unsigned long lineno,
+ int error_number,
+ unsigned flags,
+ int sys_errno);
+CATALOG catalog_create(CATALOG_ERROR_HANDLER);
+void catalog_load_file(CATALOG, const char *);
+void catalog_delete(CATALOG);
+int catalog_lookup_entity(CATALOG,
+ const char *public_id,
+ const char *name,
+ enum catalog_decl_type,
+ const char *subst_table,
+ const char **system_id,
+ const char **catalog_file);
+const char *catalog_error_text(int error_number);
+
+#else /* not __STDC__ */
+
+typedef char *CATALOG;
+typedef void (*CATALOG_ERROR_HANDLER)();
+CATALOG catalog_create();
+void catalog_load_file();
+void catalog_delete();
+int catalog_lookup_entity();
+char *catalog_error_text();
+
+#endif /* not __STDC__ */
+
+#endif /* not CATALOG_H */
diff --git a/usr.bin/sgmls/sgmls/config.h b/usr.bin/sgmls/sgmls/config.h
new file mode 100644
index 0000000..a7fa92c
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/config.h
@@ -0,0 +1,158 @@
+/* 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"
+/* A macro that returns non-zero if the filename is relative to the
+ current directory. */
+#define FILE_IS_RELATIVE(p) ((p)[0] != '/')
+/* A string containing the characters that can separate the directory
+ part of a filename from the basename. */
+#define DIR_BASE_SEP "/"
+/* The environment variable that contains the list of catalog entry files.
+ Filenames are separated by PATH_FILE_SEP. */
+#define CATALOG_FILES_ENV_VAR "SGML_CATALOG_FILES"
+/* Default list of catalog entry files. */
+#define DEFAULT_CATALOG_FILES "CATALOG:/usr/share/sgml/CATALOG"
+
+/* 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..10a123a
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/context.c
@@ -0,0 +1,451 @@
+#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 != 0) {
+ if (mexts == -1 && 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);
+ 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);
+ }
+ STATUS = (UNCH)tokenreq(gi, mod, pos);
+ TRACEGI("STATUS", gi, mod, pos);
+ if (gi==TOKEN.tu.thetd) { /* Hit in model. */
+ STATUS = (UNCH)RCHIT;
+ gtypesv = GTYPE; toccsv = TOCC;
+ newtoken(mod, pos, statuspt);
+ if (mexts <= 0)
+ return RCHIT;
+ else if (gtypesv==TTOR || BITON(toccsv, TOPT)) {
+ /* restore position */
+ copypos(pos, savedpos);
+ return RCMEX;
+ }
+ else
+ return 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);
+ 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);
+ 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);
+ }
+ 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);
+ 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);
+}
+/* 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);
+ /* 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;
+ 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);
+ 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);
+ 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);
+ 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..01f4383
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/context.h
@@ -0,0 +1,19 @@
+/* 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 Tstart pos[P].tstart /* Index of starting token in its group
+ for AND group testing. */
+#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..3e0f3bd
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/ebcdic.h
@@ -0,0 +1,25 @@
+/* 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
diff --git a/usr.bin/sgmls/sgmls/entgen.c b/usr.bin/sgmls/sgmls/entgen.c
new file mode 100644
index 0000000..2146829
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/entgen.c
@@ -0,0 +1,517 @@
+/* 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 UNIV catsysidgen P((const char *, const char *));
+static const char *basename P((const 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 */
+};
+
+static CATALOG catalog;
+
+VOID entginit(swp)
+struct switches *swp;
+{
+ catalog = swp->catalog;
+}
+
+/* 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 *qname;
+ char *file;
+ enum catalog_decl_type dtype;
+ char *subst = 0;
+ const char *sysid;
+ const char *catfile;
+
+ 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)
+ return sysidgen((char *)f->fpisysis);
+
+ qname = (char *)f->fpinm;
+
+ switch (f->fpistore) {
+ case 3:
+ /* fall through */
+ qname--; /* hack */
+ case 1:
+ case 2:
+ dtype = CATALOG_ENTITY_DECL;
+ if (ENTCASE)
+ subst = getsubst();
+ break;
+ case 4:
+ dtype = CATALOG_DOCTYPE_DECL;
+ if (NAMECASE)
+ subst = getsubst();
+ break;
+ default:
+ dtype = CATALOG_NO_DECL;
+ }
+
+ if (catalog_lookup_entity(catalog,
+ (char *)f->fpipubis,
+ qname,
+ dtype,
+ (char *)subst,
+ &sysid,
+ &catfile))
+ return catsysidgen(sysid, catfile);
+ if (f->fpisysis
+ && (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;
+}
+
+/* Handle a system id in a catalog entry file. */
+static
+UNIV catsysidgen(s, catfile)
+const char *s;
+const char *catfile;
+{
+ const char *p;
+ char *bufp;
+ char *buf;
+ int nrelative = 0;
+ int catdirlen = 0;
+ if (FILE_IS_RELATIVE(s))
+ nrelative++;
+ for (p = s; *p; p++)
+ if (*p == SYSID_FILE_SEP
+ && FILE_IS_RELATIVE(p + 1))
+ nrelative++;
+ if (nrelative) {
+ const char *base = basename(catfile);
+ catdirlen = base - catfile;
+ }
+ buf = (char *)rmalloc(p - s + 2 + nrelative*catdirlen);
+ bufp = buf;
+ for (;;) {
+ if (!*s)
+ break;
+ if (*s != SYSID_FILE_SEP && FILE_IS_RELATIVE(s)) {
+ memcpy(bufp, catfile, catdirlen);
+ bufp += catdirlen;
+ }
+ for (;;) {
+ if (*s == SYSID_FILE_SEP) {
+ s++;
+ break;
+ }
+ *bufp++ = *s++;
+ if (*s == '\0')
+ break;
+ }
+ if (bufp > buf && bufp[-1] != '\0')
+ *bufp++ = '\0';
+ }
+ if (bufp == buf) {
+ frem((UNIV)buf);
+ return 0;
+ }
+ *bufp++ = '\0';
+ return buf;
+}
+
+static
+const char *basename(s)
+const char *s;
+{
+ const char *p = s;
+ while (*p)
+ p++;
+ if (p > s) {
+ while (--p > s)
+ if (strchr(DIR_BASE_SEP, *p))
+ return p + 1;
+ }
+ return s;
+}
+
+/*
+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..84a3515
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/entity.h
@@ -0,0 +1,192 @@
+/* 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"
+#include "catalog.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. */
+ int swcap; /* 1=report capcity errors */
+ 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. */
+ CATALOG catalog; /* Catalog for generating system identifiers. */
+};
+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..8ec64c1
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/etype.h
@@ -0,0 +1,93 @@
+/* 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. */
+ UNCH tstart; /* Index of starting token for AND group
+ testing. */
+ 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..b653d14
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/genlex.c
@@ -0,0 +1,140 @@
+/* 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",
+ "lexmin"
+};
+
+#define UNUSED -1
+
+extern int iso646charset[];
+extern int iso646G0charset[];
+extern int iso646C0charset[];
+extern int iso8859_1charset[];
+extern int iso6429C1charset[];
+
+static struct {
+ char *name;
+ int *map;
+} charsets[] = {
+ { "iso646charset", iso646charset },
+ { "iso646G0charset", iso646G0charset },
+ { "iso646G0charset", iso646G0charset },
+ { "iso8859_1charset", iso8859_1charset },
+ { "iso646C0charset", iso646C0charset },
+ { "iso6429C1charset", iso6429C1charset },
+};
+
+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 charsets. */
+ for (i = 0; i < sizeof(charsets)/sizeof(charsets[0]); i++) {
+ int j;
+ int *map = charsets[i].map;
+ printf("\nint %s[] = {\n", charsets[i].name);
+ for (j = 0; j < 256; j++)
+ if (map[j] == UNUSED)
+ printf("UNUSED,%c", (j + 1) % 8 == 0 ? '\n' : ' ');
+ else
+ printf("%3d,%c", charset[map[j]], (j + 1) % 16 == 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..9a218b3
--- /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..c6df696
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/latin1.h
@@ -0,0 +1,37 @@
+/* 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
diff --git a/usr.bin/sgmls/sgmls/lexcode.h b/usr.bin/sgmls/sgmls/lexcode.h
new file mode 100644
index 0000000..d34e3e6
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/lexcode.h
@@ -0,0 +1,12 @@
+/* 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 MINLITC 13 /* LIT LITA as literal terminator in minimum data */
+#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..643b336
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/lexrf.c
@@ -0,0 +1,125 @@
+/* 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. */
+ MINLITC, /* LEXMIN: 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, lexmin, 0
+};
diff --git a/usr.bin/sgmls/sgmls/lextaba.c b/usr.bin/sgmls/sgmls/lextaba.c
new file mode 100644
index 0000000..a851d85
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/lextaba.c
@@ -0,0 +1,750 @@
+/* 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*/
+/* LEXMIN: Lexical table for minimum data literals.
+*/
+/* 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 */
+/* Either LIT or LITA changed to LITC when a literal is begun.
+ It is changed back when the LITC occurs (i.e., when the literal ends).
+*/
+UNCH lexmin[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, FRE, FRE, FRE, FRE, FRE, FRE, 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, FRE, MIN, FRE, 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, FRE, FRE, FRE, 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*/
+/* 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 iso646charset[] = {
+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,
+};
+
+/* This table maps the C0 part of ISO646 to the system character set. */
+/* We through in 32 and 127 for free, since ISO 2022 maps them in
+automatically. */
+int iso646C0charset[] = {
+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, 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, 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,
+};
+
+/* This table maps the G0 part of ISO646 to the system character set. */
+int iso646G0charset[] = {
+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,
+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,
+};
+
+int iso8859_1charset[] = {
+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,
+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,
+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,
+};
+
+int iso6429C1charset[] = {
+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,
+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,
+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..5cfe0de
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/lextabe.c
@@ -0,0 +1,357 @@
+/* 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 lexmin[] = {
+ 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, 0, 2, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
+ 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 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, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 5,
+};
+
+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 iso646charset[] = {
+ 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,
+};
+
+int iso646G0charset[] = {
+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,
+ 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,
+};
+
+int iso646G0charset[] = {
+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,
+ 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,
+};
+
+int iso8859_1charset[] = {
+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,
+ 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,
+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,
+};
+
+int iso646C0charset[] = {
+ 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, 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, 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,
+};
+
+int iso6429C1charset[] = {
+ 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,
+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,
+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..794eff8
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/lineout.c
@@ -0,0 +1,656 @@
+/* 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) {
+ putchar(' ');
+ print_string(ustrlen(ADVAL(al, aln)), ADVAL(al, aln), 0);
+ }
+ 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(' ');
+ for (; vallen > 0; --vallen, ++val)
+ putchar(*val);
+}
+
+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..25ead40
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/main.c
@@ -0,0 +1,650 @@
+/* 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"
+#include "alloc.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
+/* Message set to use for error messages from catalog.c. */
+#define CAT_SET 5
+#define CATALOG_ERROR_HEADER_MSGNO 20
+#define CATALOG_ERROR_HEADER_TEXT "Catalog error at %s, line %lu"
+
+#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 VOID do_catalog_error();
+
+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;
+static CATALOG catalog; /* Entity catalog. */
+
+char options[] = {
+ 'c', ':', 'd', 'e', 'g', 'i', ':', 'l', 'o', ':', 'p', 'r', 's', 'u', 'v',
+ 'm', ':',
+#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);
+ catalog = catalog_create(do_catalog_error);
+ swinit(&sw);
+
+ while ((opt = getopt(argc, argv, options)) != -1) {
+ switch (opt) {
+ case 'm':
+ catalog_load_file(catalog, optarg);
+ break;
+ case 'l': /* Generate location information. */
+ locsw = 1;
+ break;
+ case 'c': /* Print capacity usage. */
+ sw.swcap = 1;
+ 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] [-m file]%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->catalog = catalog;
+ swp->swambig = 1; /* Always check for ambiguity. */
+ swp->swundef = 0;
+ swp->swcap = 0; /* Don't check capacities. */
+ 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);
+}
+
+static
+VOID do_catalog_error(filename, lineno, error_number, flags, sys_errno)
+char *filename;
+unsigned long lineno;
+int error_number;
+unsigned flags;
+int sys_errno;
+{
+ char *text;
+ unsigned indent;
+ text = catgets(catd, CAT_SET, error_number,
+ (char *)catalog_error_text(error_number)); /* XXX */
+ assert(text != 0);
+ fprintf(stderr, "%s: ", prog);
+ indent = strlen(prog) + 2;
+ if (flags & CATALOG_SYSTEM_ERROR)
+ fprintf(stderr, text, filename, strerror(sys_errno));
+ else {
+ unsigned i;
+ fprintf(stderr,
+ catgets(catd, APP_SET,
+ CATALOG_ERROR_HEADER_MSGNO,
+ CATALOG_ERROR_HEADER_TEXT),
+ filename, lineno);
+ fputs(":\n", stderr);
+ for (i = 0; i < indent; i++)
+ putc(' ', stderr);
+ fputs(text, stderr);
+ }
+ putc('\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..66c476d
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/md1.c
@@ -0,0 +1,866 @@
+#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);
+ }
+#if 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);
+#endif
+}
+/* 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.
+ */
+ 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 = pcbmdtk;
+ 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 MGRP:
+ REPEATCC;
+ /* fall through */
+ 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..df7e57e
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/md2.c
@@ -0,0 +1,792 @@
+#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).
+ */
+ 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;
+ }
+ 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.
+ */
+ 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.
+ */
+ /* 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:
+ parsemd(sysibuf, NAMECASE, &pcblitc, LITLEN);
+ TRACEMD("3: sys ID literal");
+ if (pcbmd.action==LIT || pcbmd.action==LITE) {
+ entlen += ustrlen(sysibuf);
+ fpis->fpisysis = sysibuf;
+ 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;
+ parsemd(tbuf, NAMECASE, &pcblitp, LITLEN);
+ goto genfpi;
+ }
+ /* PARAMETER 5: Notation name.
+ */
+ 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.
+ */
+ 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. */
+ 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 (parsefpi(fpis)>0) {
+ if (FORMAL==YES)
+ 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=='-')
+ && p[1] == '/' && p[2] == '/') { /* If owner registered,
+ unregistered. */
+ f->fpiot = *p; /* Save owner type. */
+ p += 3;
+ }
+ 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=='-' && p[1] == '/' && p[2] == '/') { /* If text is unavailable
+ public text.*/
+ f->fpitt = *p; /* Save text type. */
+ p += 3;
+ }
+ 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. */
+ /* Language must be a name, which means it can't be empty. */
+ if (len == 0)
+ return 7;
+ 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.
+ */
+ 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 (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.
+ */
+ 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.)
+ */
+ 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;}
+ if (docelsw) {mderr(233, (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.
+ */
+ 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..5526337
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/msg.h
@@ -0,0 +1,258 @@
+/*
+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 */ {"General entity \"%s\" in short reference map \"%s\" undeclared", 'E', 'D'},
+/* 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", 'I', 'R'},
+/* 151 */ {"Could not find entity \"%2$s\" in attribute %1$s using default declaration", 'E', 'R'},
+/* 152 */ {"Short reference map \"%s\" used in USEMAP declaration but not defined; declaration will be ignored", 'E', '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 */ {"Parameter must be a number or CONTROLS or NONE", 'E', 'D'},
+/* 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", 'W', 'D'},
+/* 190 */ {"Public text class must be \"%s\"", 'W', 'D'},
+/* 191 */ {"Use of character number %s as an SGML character is not supported", 'W', 'U'},
+/* 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 not in the base character set; assuming UNUSED", '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 function \"%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 */ {"%s element cannot be excluded from %s element because it is neither inherently optional nor a member of an or group", 'E', 'C'},
+/* 217 */ {"Marked section not allowed in other prolog", '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'},
+/* 230 */ {"DSC within marked section; marked section terminated", 'E', 'C'},
+/* 231 */ {"Document element end tag can only occur in document element because entity end not allowed in other prolog", 'E', 'C'},
+/* 232 */ {"Character reference not allowed in other prolog", 'E', 'C'},
+/* 233 */ {"USEMAP declaration not allowed in other prolog", 'E', 'D'},
+/* 234 */ {"Entity reference not allowed in other prolog", 'E', 'C'},
+/* 235 */ {"Value assigned to capacity %s exceeds value assigned to TOTALCAP", 'W', 'D'},
+};
diff --git a/usr.bin/sgmls/sgmls/msgcat.c b/usr.bin/sgmls/sgmls/msgcat.c
new file mode 100644
index 0000000..5c7ee9f
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/msgcat.c
@@ -0,0 +1,840 @@
+/* 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
+
+#ifdef USE_ISASCII
+#define ISASCII(c) isascii(c)
+#else
+#define ISASCII(c) (1)
+#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((unsigned char)*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..0a67cbc
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/pars1.c
@@ -0,0 +1,984 @@
+#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. */
+ tages = es;
+ 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);
+ return RSR_;
+ case MSS_:
+ if (ts == 0) synerr(217, pcb);
+ return MSS_;
+ 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()
+{
+ if (sd.omittag && ts > 0)
+ newetd = tags[ts].tetd;
+ else if (!sd.omittag && lastetd != 0)
+ newetd = lastetd;
+ else
+ newetd = 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;
+ 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 {
+ if (mslevel > 0) {
+ sgmlerr(230, propcb, (UNCH *)0, (UNCH *)0);
+ mslevel = 0;
+ msplevel = 0;
+ }
+ 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 (dtdsw && propcb == &pcbpro) {
+ /* We've had a DTD, so check it. */
+ setdtype();
+ checkdtd();
+ }
+ if (!sw.onlypro || propcb != &pcbpro || !dtdsw)
+ sgmlerr(127, propcb, (UNCH *)0, (UNCH *)0);
+ 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 ETE_: /* End tag ended prolog */
+ REPEATCC;
+ /* fall through */
+ 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;
+ struct srh *sp;
+
+ if (sw.swundef) {
+ int i;
+ struct etd *ep;
+
+ 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);
+ else {
+ int i;
+ for (i = 1; i < lex.s.dtb[0].mapdata + 1; i++) {
+ struct entity *ecb = sp->srhsrm[i];
+ if (ecb && !ecb->estore) {
+ sgmlerr(93, (struct parse *)0,
+ ecb->ename + 1,
+ sp->srhsrm[0]->ename + 1);
+ sp->srhsrm[i] = 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. */
+ sgmlerr(216, &pcbstag, NEWGI, tags[mexts].tetd->etdgi+1);
+ /* fall through */
+ 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 ((mexts = pexmex(nextetd))>0)
+ sgmlerr(E_MEXERR, &pcbstag, nextetd->etdgi+1,
+ tags[mexts].tetd->etdgi+1);
+ 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.
+ We already gave an error. */
+ 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);
+ 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.
+ */
+ lastetd = tags[ts].tetd;
+ 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);
+ if (ts == 0) {
+ docelsw = 1; /* Finished document element. */
+ if (es > 0) sgmlerr(231, conpcb, (UNCH *)0, (UNCH *)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/pars2.c b/usr.bin/sgmls/sgmls/pars2.c
new file mode 100644
index 0000000..4249797
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/pars2.c
@@ -0,0 +1,1333 @@
+#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);
+ if (docelsw) synerr(232, pcb);
+ 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 = pcb->plex[del];/* Saved value of delimiter in lexical table. */
+ int essv = es; /* Entity stack level when literal started. */
+ UNCH datadel; /* Delimiter for CDATA/SDATA entity. */
+ int parmlen = (int)maxlen + 1; /* Working limit (to be decremented). */
+ int overflow = 0; /* Did the buffer overflow? */
+
+ pcb->plex[del] = pcb->plex == lexlms ? lex.l.litc : lex.l.minlitc;
+
+ /* The RPR_ action may cause the length of the literal to decrease by
+ 1 (this discards a final space in a minimum literal); so while
+ building the literal, the length must be allowed to grow to
+ maxlen + 1. */
+
+ 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. */
+ if (parmlen <= 0) { overflow = 1; break; }
+ *pt++ = *FPOS; --parmlen;
+ continue;
+
+ case FUN_: /* Function char found; replace with space.*/
+ if (parmlen <= 0) { overflow = 1; break; }
+ *pt++ = ' '; --parmlen;
+ continue;
+
+ case RSM_: /* Record start: ccnt=0; ++rcnt.*/
+ ++RCNT; CTRSET(RSCC);
+ if (parmlen <= 0) { overflow = 1; break; }
+ *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 < datalen + 2) {
+ entdatsw = 0;
+ overflow = 1;
+ break;
+ }
+ parmlen -= datalen + 2;
+ *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) { overflow = 1; break; }
+ parmlen -= 2;
+ 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 (!overflow && pcb->action!=TER_);
+
+ if (parmlen <= 0) {
+ --pt;
+ overflow = 1;
+ }
+ if (overflow)
+ sgmlerr(134, pcb, ntoa((int)maxlen),(UNCH *)0);
+
+ datalen = (UNS)(pt-tbuf);/* To return PI string to text processor. */
+ *pt++ = EOS;
+ pcb->plex[del] = lexsv; /* Restore normal delimiter handling. */
+ if (es!=essv) synerr(37, pcb);
+}
+
+/* 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. Only
+tokenization should be done, not attribute value interpretation. */
+
+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 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);
+ if (tokenlen > NAMELEN) pcb->newstate = 0;
+ 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;
+ case EE_:
+ if (es < mdessv) {
+ synerr(37, &pcbmd);
+ mdessv = es;
+ }
+ 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..554fdfb
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/pcbrf.c
@@ -0,0 +1,1351 @@
+/* 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 ,ET0 ,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_,DAF_,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 ,ET0 ,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_,DAF_,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_,SR25,DCE_,SR11,DCE_,LAS_,FCE_},
+
+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. */
+#define EE7 8 /* TAGO, ETI 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 ,EE7 ,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_,LAM_,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_},
+
+ee7 []={ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,EE7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,
+ ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 },/*ee7*/
+ee7a[]={LAF_,LAF_,LAF_,ETE_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,ETE_,LAF_},
+
+*protab[] = {et7, et7a, es7, es7a, md7, md7a, mc7, mc7a, ee7, ee7a};
+struct parse pcbpro = {"PRO", lexcon, protab, 0, 0, 0, 0};
+#undef ET7
+#undef ES7
+#undef MD7
+#undef MC7
+#undef EE7
+/* 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 ,
+ SP1 ,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
+ litc */
+ls10 []={VA0 ,VA0 ,VA0 ,VA0 ,LS0 ,VA0 ,LS0 ,LS0 ,LS0 ,LS0 ,LS0 ,VA0 ,VA0 ,
+ LS0 },/*ls0*/
+ls10a[]={MLE_,MLA_,MLA_,MLA_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,MLE_,SYS_,SYS_,
+ TER_},
+va10 []={VA0 ,VA0 ,VA0 ,VA0 ,SP0 ,VA0 ,VA0 ,VA0 ,VA0 ,SP0 ,SP0 ,VA0 ,VA0 ,
+ LS0 },/*va0*/
+da10a[]={MLE_,MLA_,MLA_,MLA_,MLA_,SYS_,EOF_,GET_,RS_ ,FUN_,MLE_,SYS_,SYS_,
+ TER_},
+sp10 []={VA0 ,VA0 ,VA0 ,VA0 ,SP0 ,VA0 ,VA0 ,SP0 ,SP0 ,SP0 ,SP0 ,VA0 ,VA0 ,
+ LS0 },/*sp0*/
+sp10a[]={MLE_,MLA_,MLA_,MLA_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,MLE_,SYS_,SYS_,
+ RPR_},
+
+*litvtab[] = {ls10, ls10a, va10, da10a, sp10, sp10a};
+struct parse pcblitv = {"LITV", lexmin, 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 (but not -). */
+#define SP2 2 /* Separator before token expected. */
+#define TK1 4 /* Token expected. */
+#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 ,SP1 ,SP1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,SP1 ,PR1 ,PX1 ,SP1 ,RN1 ,SP1 ,SP1 ,SP1 },
+sp21a[]={INV_,LEN_,LEN_,LEN_,NOP_,SYS_,EE_ ,GET_,RS_ ,LEN_,INV_,GRPS,LIT ,LITE,
+ MDS ,INV_,NOP_,NOP_,INV_,NOP_,EMD ,INV_,INV_},
+
+sp22 []={SP2 ,SP2 ,SP2 ,SP2 ,TK1 ,SP2 ,TK1 ,SP2 ,TK1 ,CM1 ,SP2 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,SP2 ,PR1 ,PX1 ,SP2 ,RN1 ,SP2 ,SP2 ,SP2 },
+sp22a[]={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 ,SP2 ,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 */
+
+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 ,SP2 ,PR1 ,TK1 ,SP2 ,SP1 ,SP1 ,SP1 ,SP1 ,
+ SP1 ,SP1 ,SP2 ,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, sp22, sp22a, tk21, tk21a, 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 SP2
+#undef TK1
+#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_ ,LEN_,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_ ,NTV ,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_ ,LEN_,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 ,AN1 ,SP1 ,SP1 ,SP1 ,SP1 ,
+ SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,AV1 },
+vi41a[]={INV_,NASV,NASV,NASV,NOP_,SYS_,EOF_,GET_,RS_ ,NASV,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_ ,AVU ,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 (but not -) */
+#define SP2 2 /* Separator before token expected. */
+#define TK1 4 /* Token expected. */
+#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 ,SP1 ,TK1 ,TK1 ,SP1 },
+sp31a[]={INV_,ISIG,LEN_,LEN_,NOP_,SYS_,EOF_,GET_,RS_ ,LEN_,LIT1,LIT2,ESGD},
+
+sp32 []={SP2 ,SP2 ,SP2 ,SP2 ,TK1 ,SP2 ,SP2 ,SP2 ,TK1 ,CM1 ,TK1 ,TK1 ,SP2 },
+sp32a[]={INV_,ISIG,LEN_,LEN_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,LIT1,LIT2,ESGD},
+
+tk31 []={TK1 ,TK1 ,SP2 ,SP1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,CM1 ,TK1 ,TK1 ,SP1 },
+tk31a[]={INV_,ISIG,NUM1,NAS1,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,LIT1,LIT2,ESGD},
+
+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, sp32, sp32a, tk31, tk31a, cm31, cm31a, cm32, cm32a,
+ cm33, cm33a};
+
+struct parse pcbsd = {"SD", lexsd, sdtab, 0, 0, 0, 0};
+
+#undef SP1
+#undef SP2
+#undef TK1
+#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..a057d24
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/portproc.c
@@ -0,0 +1,105 @@
+/* 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"
+#include "alloc.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..b9699d2
--- /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..c138c9f
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgml1.c
@@ -0,0 +1,485 @@
+#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 ,NR2 ,ST1 ,NR2 ,ST1 },/*st2*/
+ {CON_,ETG_,MD_ ,MDC_,MSS_,MSE_,PIS_,LOP_,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);
+ entginit(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;
+
+ if (sw.swcap) {
+ 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();
+}
+
+char *getsubst()
+{
+ return (char *)lextran;
+}
+
+/* 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..df75b6a
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgml2.c
@@ -0,0 +1,522 @@
+/* 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) {
+ sgmlerr(35, (struct parse *)0, ename+1, (UNCH *)0);
+ return(ENTUNDEF);
+ }
+ else
+ ecb = usedef(ename);
+ }
+ 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 (docelsw) sgmlerr(234, (struct parse *)0, (UNCH *)0, (UNCH *)0);
+ /* 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);
+ /* Ignore reference to empty CDATA entity. */
+ if (datalen == 0 && ecb->estore == ESC) return(0);
+ 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). */
+ if (es == tages)
+ 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)
+ sgmlerr(150, (struct parse *)0, ename + 1, (UNCH *)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. */
+ if (es == tages)
+ 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);
+}
+/* SAVMDERR: Save an md error for possible later use.
+*/
+UNIV savmderr(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);
+ 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..6073e66
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmlaux.h
@@ -0,0 +1,72 @@
+/* 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 entginit P((struct switches *));
+
+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 *));
+char *getsubst P((void));
diff --git a/usr.bin/sgmls/sgmls/sgmldecl.c b/usr.bin/sgmls/sgmls/sgmldecl.c
new file mode 100644
index 0000000..6ef6b68
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmldecl.c
@@ -0,0 +1,1804 @@
+/* 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_SHUNCHAR 159
+#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 E_TOTALCAP 235
+
+#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,
+};
+
+/* This is a private use designating sequence that by convention
+refers to the whole system character set whatever it is. */
+
+#define SYSTEM_CHARSET_DESIGNATING_SEQUENCE "ESC 2/5 2/15 3/0"
+
+static struct pmap charset_map[] = {
+ { "ESC 2/5 4/0", (UNIV)iso646charset }, /* ISO 646 IRV */
+ { "ESC 2/8 4/2", (UNIV)iso646G0charset }, /* ISO Registration Number 6, ASCII */
+ { "ESC 2/8 4/0", (UNIV)iso646G0charset }, /* ISO Registration Number 6, ASCII */
+ { "ESC 2/13 4/1", (UNIV)iso8859_1charset }, /* Latin 1 */
+ { "ESC 2/1 4/0", (UNIV)iso646C0charset }, /* ISO 646, C0 */
+ { "ESC 2/2 4/3", (UNIV)iso6429C1charset }, /* ISO 6429, C1 */
+ { 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 */
+#define MAX_SAVED_ERRS 4
+static UNIV saved_errs[MAX_SAVED_ERRS];
+static int nsaved_errs = 0;
+
+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 *, int));
+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 sdsaverr P((UNS, UNCH *, 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;
+ nsaved_errs = 0;
+ for (i = 0; i < SIZEOF(section); i++)
+ if ((*section[i])(tbuf) == FAIL) {
+ errsw = 1;
+ break;
+ }
+ if (sd.formal) {
+ /* print saved errors */
+ int i;
+ for (i = 0; i < nsaved_errs; i++)
+ svderr(saved_errs[i]);
+ }
+ else {
+ /* free saved errors */
+ int i;
+ for (i = 0; i < nsaved_errs; i++)
+ msgsfree(saved_errs[i]);
+ }
+
+ 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, 0);
+ 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;
+
+#if 0
+ 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
+ }
+#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) {
+#if 0
+ sderr(E_CHARMISSING, ltous((long)i), (UNCH *)0);
+#endif
+ 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))
+ sdsaverr(E_FORMAL, (UNCH *)0, (UNCH *)0);
+ else if (fpi.fpic != FPICHARS)
+ sdsaverr(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);
+ 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;
+ int i;
+
+ 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;
+ }
+ for (i = 1; i < NCAPACITY; i++)
+ if (sd.capacity[i] > sd.capacity[0])
+ sderr(E_TOTALCAP, (UNCH *)captab[i], (UNCH *)0);
+ 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, 1);
+ 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, 1);
+ 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_SHUNCHAR, (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, silently)
+UNCH *tbuf;
+int silently;
+{
+ if (strncmp((char *)tbuf, "ISO 8879-1986", 13) == 0) {
+ if (!silently)
+ 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 (*p == lexmin) {
+ /* If it used to be NONSGML, but its now significant,
+ treat it like a datachar. */
+ if ((*p)[i] == nonclass)
+ (*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 \"-//Dummy//CHARSET Dummy//%s\"\nDESCSET\n",
+ 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)
+ break;
+ if (i == 256)
+ fprintf(fp, " NONE\n");
+ else {
+ for (; i < 256; i++)
+ if (char_flags[i] & CHAR_SHUNNED)
+ fprintf(fp, " %d", i);
+ fprintf(fp, "\n");
+ }
+
+ fprintf(fp,
+ "BASESET \"-//Dummy//CHARSET Dummy//%s\"\nDESCSET 0 256 0\n",
+ SYSTEM_CHARSET_DESIGNATING_SEQUENCE);
+
+ fprintf(fp, "FUNCTION\nRE %d\nRS %d\nSPACE %d\nTAB SEPCHAR %d\n",
+ RECHAR, RSCHAR, ' ', TABCHAR);
+
+ 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");
+}
+
+/* Save an error to be printed only if FORMAL is declared as YES. */
+
+static
+VOID sdsaverr(number, parm1, parm2)
+UNS number;
+UNCH *parm1;
+UNCH *parm2;
+{
+ saved_errs[nsaved_errs++] = savmderr(number, parm1, parm2);
+}
+
+/*
+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..1111f72
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmldecl.h
@@ -0,0 +1,90 @@
+/* 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 iso646charset[];
+extern int iso646G0charset[];
+extern int iso646C0charset[];
+extern int iso8859_1charset[];
+extern int iso6429C1charset[];
+
+
diff --git a/usr.bin/sgmls/sgmls/sgmlfnsm.h b/usr.bin/sgmls/sgmls/sgmlfnsm.h
new file mode 100644
index 0000000..3003d67
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmlfnsm.h
@@ -0,0 +1,130 @@
+/* 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 *));
+UNIV savmderr P((unsigned int,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..c78bb7a
--- /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..4d98c55
--- /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", (char *)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..bd92c92
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmls.1
@@ -0,0 +1,970 @@
+'\" $Id$
+'\" 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
+]
+[
+.BI \-m file
+]
+.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
+Report any capacity limits that are exceeded
+and 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
+.BI \-m file
+Map public identifiers and entity names to system identifiers
+using the catalog entry file
+.IR file .
+Multiple
+.B \-m
+options are allowed.
+Catalog entry files specified with the
+.B -m
+option will be searched before the defaults.
+.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.
+.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.
+.LP
+If a system identifier is not specified,
+then the entity manager can generate one using catalog
+entry files in the format defined in the SGML Open Draft Technical
+Resolution on Entity Management. A catalog entry file contains a
+sequence of entries in one of the following four forms:
+.TP
+.BI PUBLIC\ pubid\ sysid
+This specifies that
+.I sysid
+should be used as the system identifier if the public
+identifier is
+.IR pubid .
+.I Sysid
+is a system identifier as defined in ISO 8879 and
+.I pubid
+is a public identifier as defined in ISO 8879.
+.TP
+.BI ENTITY\ name\ sysid
+This specifies that
+.I sysid
+should be used as the system identifier if the entity is a general
+entity whose name is
+.IR name .
+.TP
+.BI ENTITY\ % name\ sysid
+This specifies that
+.I sysid
+should be used as the system identifier if the entity is a parameter
+entity whose name is
+.IR name .
+Note that there is no space between the
+.B %
+and the
+.IR name .
+.TP
+.BI DOCTYPE\ name\ sysid
+This specifies that
+.I sysid
+should be used as the system identifier if the entity is an
+entity declared in a document type declaration whose document type name is
+.IR name .
+.LP
+The last two forms are extensions to the SGML Open format.
+The delimiters can be omitted from the
+.I sysid
+provided it does not contain any white space.
+Comments are allowed between parameters delimited by
+.B --
+as in SGML.
+The environment variable
+.B \s-1SGML_CATALOG_FILES\s0
+contains a
+.if \n(Os=0 colon-separated
+.if \n(Os=1 semicolon-separated
+list of catalog entry files.
+These will be searched after any catalog entry files specified
+using the
+.B \-m
+option.
+If this environment variable is not set,
+then a system dependent list of catalog entry files will be used.
+A match in a catalog entry file for a PUBLIC entry will take
+precedence over a match in the same file for an ENTITY
+or DOCTYPE entry.
+A filename in a system identifier in a catalog entry file
+is interpreted relative to the directory containing the catalog
+entry file.
+.LP
+If no match can be found in a catalog entry file, 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.
+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.
+.LP
+Normally if the external identifier for an entity includes a system
+identifier, the entity manager will use the specified system
+identifier and not attempt to generate one.
+If, however,
+.B \s-1SGML_PATH\s0
+uses the
+.B %S
+field,
+then the entity manager will first search for a matching
+entry in the catalog entry files.
+If a match is found, then this will be used instead of the
+specified system identifier.
+Otherwise,
+if the specified system identifier does not contain any
+.if \n(Os=0 colons,
+.if \n(Os=1 semi-colons,
+the entity manager will use
+.B \s-1SGML_PATH\s0
+to generate a filename.
+Otherwise the entity manager will use the specified system identifier.
+.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
+Exceeding a capacity limit will be ignored unless the
+.B \-c
+option is given.
+.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.
+.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.
+.LP
+.I Sgmls
+identifies base character sets using the designating sequence in the
+public identifier. The following designating sequences are
+recognized:
+.TS
+tab(&);
+c c c c c
+c c c c ^
+c c c c ^
+l n n n l.
+Designating&ISO&Minimum&Number&Description
+Escape&Registration&Character&of&
+Sequence&Number&Number&Characters&
+_
+ESC 2/5 4/0&-&0&128&full set of ISO 646 IRV
+ESC 2/8 4/0&2&33&94&G0 set of ISO 646 IRV
+ESC 2/8 4/2&6&33&94&G0 set of ASCII
+ESC 2/13 4/1&100&32&96&G1 set of ISO 8859-1
+ESC 2/1 4/0&1&0&32&C0 set of ISO 646
+ESC 2/2 4/3&77&0&32&C1 set of ISO 6429
+ESC 2/5 2/15 3/0&-&0&256&the system character set
+.TE
+.LP
+When one of the G0 sets is used as a base set, the characters SPACE
+and DELETE are treated as occurring at positions 32 and 127
+respectively; although these characters are not part of the character
+sets designated by the escape sequences, this mimics the behaviour of
+ISO 2022 with respect to these code positions.
+.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..74d7894
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmlxtrn.c
@@ -0,0 +1,225 @@
+/* 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 docelsw = 0; /* 1=had document element; 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 tages = -1; /* ES level at start of tag. */
+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 *lastetd = 0; /* most recently ended ETD. */
+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). */
+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..e551200
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmlxtrn.h
@@ -0,0 +1,123 @@
+/* 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 docelsw; /* 1=had document element; 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 tages; /* ES level at start of tag. */
+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 *lastetd; /* Most recently ended ETD. */
+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 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..4e6e856
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/std.h
@@ -0,0 +1,110 @@
+/* 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 */
+
+extern char *strerror();
+
+#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..1cdf9a0
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/synxtrn.h
@@ -0,0 +1,154 @@
+/* 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 minlitc; /* LEXMIN: 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 lexmin[]; /* Lexical table: minimum data literal. */
+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..f917a26
--- /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));
+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 *));
+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) \
+ ((void)(ctrace && (traceend(stagenm, mod, pos, rc, opt), 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) \
+ ((void)(ctrace && (tracegi(stagenm, gi, mod, pos), 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) /* empty */
+#define TRACEESN(p) /* empty */
+#define TRACEETG(pts, curetd, tsl, etagimct) /* empty */
+#define TRACEETD(p) /* empty */
+#define TRACEGI(stagenm, gi, mod, pos) /* 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..e57003f
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/traceset.c
@@ -0,0 +1,462 @@
+#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, %lu:%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, %lu:%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, "=>");
+ tracedcn(ADDATA(al,i).x);
+ }
+ }
+ 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)
+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 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)
+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 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..f3b2d7c
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/version.c
@@ -0,0 +1 @@
+char *version_string = "1.1.91";
diff --git a/usr.bin/sgmls/sgmls/xfprintf.c b/usr.bin/sgmls/sgmls/xfprintf.c
new file mode 100644
index 0000000..1c50469
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/xfprintf.c
@@ -0,0 +1,564 @@
+/* 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 */
+
+#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/unix.cfg b/usr.bin/sgmls/unix.cfg
new file mode 100644
index 0000000..4245511
--- /dev/null
+++ b/usr.bin/sgmls/unix.cfg
@@ -0,0 +1,165 @@
+/* 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"
+/* A macro that returns non-zero if the filename is relative to the
+ current directory. */
+#define FILE_IS_RELATIVE(p) ((p)[0] != '/')
+/* A string containing the characters that can separate the directory
+ part of a filename from the basename. */
+#define DIR_BASE_SEP "/"
+/* The environment variable that contains the list of catalog entry files.
+ Filenames are separated by PATH_FILE_SEP. */
+#define CATALOG_FILES_ENV_VAR "SGML_CATALOG_FILES"
+/* Default list of catalog entry files. */
+#define DEFAULT_CATALOG_FILES "CATALOG:/usr/local/lib/sgml/CATALOG"
+
+/* 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 your compiler doesn't understand const, define it to be nothing. */
+#ifndef __STDC__
+#ifndef const
+#define const /* as nothing */
+#endif
+#endif
+
+/* 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..6d06623
--- /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}${BINDIR}/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..b85f7ce
--- /dev/null
+++ b/usr.bin/shar/shar.1
@@ -0,0 +1,110 @@
+.\" 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
+.\" $Id$
+.\"
+.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
+The
+.Nm
+command writes a
+.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
+The
+.Nm
+command is normally used for distributing files by
+.Xr ftp 1
+or
+.Xr mail 1 .
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr mail 1 ,
+.Xr tar 1 ,
+.Xr uuencode 1
+.Sh BUGS
+The
+.Nm
+command makes no provisions for special types of files or files containing
+magic characters.
+The
+.Nm
+command cannot handle files without a newline ('\\n')
+as the last character.
+.Pp
+It is easy to insert trojan horses into
+.Nm
+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
+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 appeared 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..6c466da
--- /dev/null
+++ b/usr.bin/showmount/showmount.8
@@ -0,0 +1,94 @@
+.\" 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 8 ,
+.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
+.Bx 4.4 .
diff --git a/usr.bin/showmount/showmount.c b/usr.bin/showmount/showmount.c
new file mode 100644
index 0000000..b4896e2
--- /dev/null
+++ b/usr.bin/showmount/showmount.c
@@ -0,0 +1,356 @@
+/*
+ * 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/queue.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")) != -1)
+ 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 [-ade3] 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..ee46bb2
--- /dev/null
+++ b/usr.bin/size/size.1
@@ -0,0 +1,61 @@
+.\" 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
+.At v6 .
diff --git a/usr.bin/size/size.1aout b/usr.bin/size/size.1aout
new file mode 100644
index 0000000..ee46bb2
--- /dev/null
+++ b/usr.bin/size/size.1aout
@@ -0,0 +1,61 @@
+.\" 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
+.At v6 .
diff --git a/usr.bin/size/size.c b/usr.bin/size/size.c
new file mode 100644
index 0000000..b34e24c
--- /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, "")) != -1)
+ 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/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..6b75c75
--- /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:")) != -1)
+ 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..2ddeced
--- /dev/null
+++ b/usr.bin/strings/strings.c
@@ -0,0 +1,221 @@
+/*
+ * 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 <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define DEF_LEN 4 /* default minimum string length */
+#define ISSTR(ch) (isalnum(ch) || ispunct(ch) || \
+ isspace(ch) && (!iscntrl(ch) || ch == '\t') || \
+ isascii(ch) && isprint(ch))
+
+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;
+
+ (void) setlocale(LC_CTYPE, "");
+
+ /*
+ * 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")) != -1)
+ 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..3d19864
--- /dev/null
+++ b/usr.bin/strip/Makefile
@@ -0,0 +1,21 @@
+# @(#)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 defined(STRIP)
+.if ${STRIP:M-s} != ""
+ ./strip maybe_stripped
+.endif
+.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..d9252ac
--- /dev/null
+++ b/usr.bin/strip/strip.1
@@ -0,0 +1,73 @@
+.\" 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
+.\" $Id$
+.\"
+.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 v1 .
diff --git a/usr.bin/strip/strip.1aout b/usr.bin/strip/strip.1aout
new file mode 100644
index 0000000..d9252ac
--- /dev/null
+++ b/usr.bin/strip/strip.1aout
@@ -0,0 +1,73 @@
+.\" 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
+.\" $Id$
+.\"
+.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 v1 .
diff --git a/usr.bin/strip/strip.c b/usr.bin/strip/strip.c
new file mode 100644
index 0000000..564a9a3
--- /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.9 1997/02/22 19:57:14 peter 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")) != -1)
+ 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 *)MAP_FAILED) {
+ 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 [-dx] 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..5c0782c
--- /dev/null
+++ b/usr.bin/su/Makefile
@@ -0,0 +1,37 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+
+PROG= su
+SRCS= su.c
+
+#LC_AUTH=-DLOGIN_CAP_AUTH
+COPTS+= -DLOGIN_CAP $(LC_AUTH)
+LDADD+= -lutil
+DPADD+= ${LIBUTIL}
+
+.if !defined(LC_AUTH)
+COPTS+= -DSKEY
+LDADD+= -lskey -lcrypt
+DPADD+= ${LIBSKEY} ${LIBCRYPT}
+.endif
+
+.if defined(WHEELSU)
+COPTS+= -DWHEELSU
+.endif
+CFLAGS+= -Wall
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \
+ || defined(MAKE_EBONES)) && !defined(LC_AUTH)
+CFLAGS+=-DKERBEROS
+DPADD+= ${LIBKRB} ${LIBDES}
+LDADD+= -lkrb -ldes
+DISTRIBUTION= krb
+.endif
+
+LDADD+= -lmd
+DPADD+= ${LIBMD}
+
+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..0c60ec7
--- /dev/null
+++ b/usr.bin/su/su.1
@@ -0,0 +1,210 @@
+.\" 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
+.\" $Id: su.1,v 1.7 1997/02/22 19:57:15 peter Exp $
+.\"
+.\" 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 .
+Resource limits and session priority applicable to the original user's
+login class (See
+.Xr login.conf 5 )
+are also normally retained unless the target login as a user ID of 0.
+.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.
+Envronment variables may be set or overridden from the login class
+capabilities database according to the class of the target login.
+The invoked shell is the target login's, and
+.Nm su
+will change directory to the target login's home directory.
+Resource limits and session priority are modified to that for the
+target account's login class.
+.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 listed in group 0 (normally
+.Dq wheel )
+can
+.Nm su
+to
+.Dq root ,
+unless this group is empty.
+.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 kerberos 1 ,
+.Xr kinit 1 ,
+.Xr login 1 ,
+.Xr sh 1 ,
+.Xr group 5 ,
+.Xr login.conf 5 ,
+.Xr passwd 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 v1 .
diff --git a/usr.bin/su/su.c b/usr.bin/su/su.c
new file mode 100644
index 0000000..fb137d8
--- /dev/null
+++ b/usr.bin/su/su.c
@@ -0,0 +1,564 @@
+/*
+ * 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 const 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";
+*/
+static const char rcsid[] =
+ "$Id: su.c,v 1.19 1997/03/29 04:32:40 imp Exp $";
+#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 LOGIN_CAP
+#include <login_cap.h>
+#ifdef LOGIN_CAP_AUTH
+#undef SKEY
+#undef KERBEROS
+#endif
+#endif
+
+#ifdef SKEY
+#include <skey.h>
+#endif
+
+#ifdef KERBEROS
+#include <des.h>
+#include <kerberosIV/krb.h>
+#include <netdb.h>
+
+#define ARGSTR "-Kflm"
+
+static int kerberos(char *username, char *user, int uid, char *pword);
+static int koktologin(char *name, char *toname);
+
+int use_kerberos = 1;
+#else /* !KERBEROS */
+#define ARGSTR "-flm"
+#endif /* KERBEROS */
+
+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=NULL, *username, *cleanenv[20], **nargv, **np;
+ struct group *gr;
+ uid_t ruid;
+ int asme, ch, asthem, fastlogin, prio, i;
+ enum { UNSET, YES, NO } iscsh = UNSET;
+#ifdef LOGIN_CAP
+ login_cap_t *lc;
+ int setwhat;
+#ifdef LOGIN_CAP_AUTH
+ char *style, *approvep, *auth_method = NULL;
+#endif
+#endif
+ char shellbuf[MAXPATHLEN];
+
+#ifdef WHEELSU
+ iswheelsu =
+#endif /* WHEELSU */
+ asme = asthem = fastlogin = 0;
+ user = "root";
+ while(optind < argc)
+ if((ch = getopt(argc, argv, ARGSTR)) != -1)
+ 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 != NULL && *pwd->pw_shell != '\0') {
+ /* copy: pwd memory is recycled */
+ shell = strncpy(shellbuf, pwd->pw_shell, sizeof shellbuf);
+ shellbuf[sizeof shellbuf - 1] = '\0';
+ } else {
+ shell = _PATH_BSHELL;
+ iscsh = NO;
+ }
+ }
+
+#ifdef LOGIN_CAP_AUTH
+ if (auth_method = strchr(user, ':')) {
+ *auth_method = '\0';
+ auth_method++;
+ if (*auth_method == '\0')
+ auth_method = NULL;
+ }
+#endif /* !LOGIN_CAP_AUTH */
+
+ /* get target login information, default to root */
+ if ((pwd = getpwnam(user)) == NULL) {
+ errx(1, "unknown login: %s", user);
+ }
+#ifdef LOGIN_CAP
+ lc = login_getpwclass(pwd);
+#endif
+
+#ifdef WHEELSU
+ targetpass = strdup(pwd->pw_passwd);
+#endif /* WHEELSU */
+
+ if (ruid) {
+#ifdef KERBEROS
+ if (use_kerberos && koktologin(username, user)
+ && !pwd->pw_uid) {
+ warnx("kerberos: not in %s's ACL.", user);
+ use_kerberos = 0;
+ }
+#endif
+ {
+ /* only allow those in group zero to su to root. */
+ if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)) &&
+ gr->gr_mem && *(gr->gr_mem))
+ 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 LOGIN_CAP_AUTH
+ /*
+ * This hands off authorisation to an authorisation program,
+ * depending on the styles available for the "auth-su",
+ * authorisation styles.
+ */
+ if ((style = login_getstyle(lc, auth_method, "su")) == NULL)
+ errx(1, "auth method available for su.\n");
+ if (authenticate(user, lc ? lc->lc_class : "default", style, "su") != 0) {
+#ifdef WHEELSU
+ if (!iswheelsu || authenticate(username, lc ? lc->lc_class : "default", style, "su") != 0) {
+#endif /* WHEELSU */
+ {
+ fprintf(stderr, "Sorry\n");
+ syslog(LOG_AUTH|LOG_WARNING,"BAD SU %s to %s%s", username, user, ontty());
+ exit(1);
+ }
+ }
+
+ /*
+ * If authentication succeeds, run any approval
+ * program, if applicable for this class.
+ */
+ approvep = login_getcapstr(lc, "approve", NULL, NULL);
+ if (approvep==NULL || auth_script(approvep, approvep, username, lc->lc_class, 0) == 0) {
+ int r = auth_scan(AUTH_OKAY);
+ /* See what the authorise program says */
+ if (!(r & AUTH_ROOTOKAY) && pwd->pw_uid == 0) {
+ fprintf(stderr, "Sorry\n");
+ syslog(LOG_AUTH|LOG_WARNING,"UNAPPROVED ROOT SU %s%s", user, ontty());
+ exit(1);
+ }
+ }
+#else /* !LOGIN_CAP_AUTH */
+#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
+#ifdef KERBEROS
+ if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p)))
+#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 */
+#endif /* LOGIN_CAP_AUTH */
+ }
+ 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) {
+ p = strrchr(shell, '/');
+ if (p)
+ ++p;
+ else
+ p = shell;
+ if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO)
+ iscsh = strcmp(p, "tcsh") ? NO : YES;
+ }
+
+ (void)setpriority(PRIO_PROCESS, 0, prio);
+
+#ifdef LOGIN_CAP
+ /* Set everything now except the environment & umask */
+ setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
+ /*
+ * Don't touch resource/priority settings if -m has been
+ * used or -l hasn't, and we're not su'ing to root.
+ */
+ if ((asme || !asthem) && pwd->pw_uid)
+ setwhat &= ~(LOGIN_SETPRIORITY|LOGIN_SETRESOURCES);
+ if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
+ err(1, "setusercontext");
+#else
+ /* 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");
+#endif
+
+ if (!asme) {
+ if (asthem) {
+ p = getenv("TERM");
+ cleanenv[0] = NULL;
+ environ = cleanenv;
+#ifdef LOGIN_CAP
+ /* set the su'd user's environment & umask */
+ setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
+#else
+ (void)setenv("PATH", _PATH_DEFPATH, 1);
+#endif
+ if (p)
+ (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());
+
+ login_close(lc);
+
+ execv(shell, np);
+ err(1, "%s", shell);
+}
+
+int
+chshell(sh)
+ char *sh;
+{
+ int r = 0;
+ char *cp;
+
+ setusershell();
+ while (!r && (cp = getusershell()) != NULL)
+ r = strcmp(cp, sh) == 0;
+ endusershell();
+ return r;
+}
+
+char *
+ontty()
+{
+ char *p;
+ static char buf[MAXPATHLEN + 4];
+
+ buf[0] = 0;
+ p = ttyname(STDERR_FILENO);
+ if (p)
+ snprintf(buf, sizeof(buf), " on %s", p);
+ return (buf);
+}
+
+#ifdef KERBEROS
+int
+kerberos(username, user, uid, pword)
+ char *username, *user;
+ int uid;
+ char *pword;
+{
+ extern char *krb_err_txt[];
+ KTEXT_ST ticket;
+ AUTH_DAT authdata;
+ 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);
+ (void)sprintf(krbtkfile, "%s_%s_%lu", TKT_ROOT, user,
+ (unsigned long)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, pword);
+
+ 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);
+}
+
+int
+koktologin(name, toname)
+ char *name, *toname;
+{
+ AUTH_DAT *kdata;
+ AUTH_DAT kdata_st;
+ char realm[REALM_SZ];
+
+ if (krb_get_lrealm(realm, 1) != KSUCCESS)
+ return (1);
+ kdata = &kdata_st;
+ memset((char *)kdata, 0, sizeof(*kdata));
+ (void)strncpy(kdata->pname, name, sizeof kdata->pname - 1);
+ (void)strncpy(kdata->pinst,
+ ((strcmp(toname, "root") == 0) ? "root" : ""), sizeof kdata->pinst - 1);
+ (void)strncpy(kdata->prealm, realm, sizeof kdata->prealm - 1);
+ 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..a609799
--- /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 1 ,
+.Xr strip 1 ,
+.Xr nlist 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..0686993
--- /dev/null
+++ b/usr.bin/symorder/symorder.c
@@ -0,0 +1,354 @@
+/*
+ * 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:")) != -1)
+ 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");
+
+ i = n / sizeof(struct nlist);
+ if (!clean) {
+ newtab = (struct nlist *)malloc(n);
+ if (newtab == (struct nlist *)NULL)
+ error(NULL);
+ memset(newtab, 0, n);
+
+ reorder(symtab, newtab, i);
+ free((void *)symtab);
+ symtab = newtab;
+ } else {
+ symkept = i;
+ }
+
+ 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;
+ } else if (clean)
+ symfound++;
+ 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 [-c] [-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..eabc3ce
--- /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
+DPADD= ${LIBCURSES} ${LIBTERMCAP} ${LIBM} ${LIBKVM}
+LDADD= -lcurses -ltermcap -lm -lkvm
+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..bbdb58e
--- /dev/null
+++ b/usr.bin/systat/cmds.c
@@ -0,0 +1,196 @@
+/*-
+ * 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
+#if 0
+static char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 4/29/95";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#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..33e9969
--- /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 = INSET;
+ for (i = 0; i < dk_ndrive; i++)
+ if (dk_select[i] && dk_mspw[i] != 0.0) {
+ if (col + COLWIDTH >= wnd->maxx - INSET) {
+ col = INSET, 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, "%-4.4s 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 = INSET;
+ 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 - INSET) {
+ col = INSET, 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..78dd716
--- /dev/null
+++ b/usr.bin/systat/main.c
@@ -0,0 +1,296 @@
+/*-
+ * 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 <locale.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];
+
+ (void) setlocale(LC_TIME, "");
+
+ 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..8ee325f
--- /dev/null
+++ b/usr.bin/systat/mbufs.c
@@ -0,0 +1,186 @@
+/*-
+ * 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";
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mbuf.h>
+#include <sys/sysctl.h>
+
+#include <stdlib.h>
+#include <string.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 (i == MT_FREE)
+ continue;
+ 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_mbufs -= mb->m_mtypes[index];
+ mb->m_mtypes[index] = 0;
+ }
+ if (mb->m_mbufs) {
+ mvwprintw(wnd, 1+j, 0, "%-10.10s", "free");
+ if (mb->m_mbufs > 60) {
+ sprintf(buf, " %d", mb->m_mbufs);
+ mb->m_mbufs = 60;
+ while (mb->m_mbufs--)
+ waddch(wnd, 'X');
+ waddstr(wnd, buf);
+ } else {
+ while(mb->m_mbufs--)
+ waddch(wnd, 'X');
+ }
+ wclrtoeol(wnd);
+ j++;
+ }
+ wmove(wnd, 1+j, 0); wclrtobot(wnd);
+}
+
+int
+initmbufs()
+{
+ size_t len;
+ int name[3];
+
+ name[0] = CTL_KERN;
+ name[1] = KERN_IPC;
+ name[2] = KIPC_MBSTAT;
+ len = 0;
+ if (sysctl(name, 3, 0, &len, 0, 0) < 0) {
+ error("sysctl getting mbstat size failed");
+ return 0;
+ }
+
+ if (mb == 0)
+ mb = (struct mbstat *)calloc(1, sizeof *mb);
+ return 1;
+}
+
+void
+fetchmbufs()
+{
+ int name[3];
+ size_t len;
+
+ name[0] = CTL_KERN;
+ name[1] = KERN_IPC;
+ name[2] = KIPC_MBSTAT;
+ len = sizeof *mb;
+
+ if (sysctl(name, 3, mb, &len, 0, 0) < 0)
+ return;
+}
diff --git a/usr.bin/systat/netcmds.c b/usr.bin/systat/netcmds.c
new file mode 100644
index 0000000..94dc3ca
--- /dev/null
+++ b/usr.bin/systat/netcmds.c
@@ -0,0 +1,312 @@
+/*-
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Common network command support routines.
+ */
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.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..b8be493
--- /dev/null
+++ b/usr.bin/systat/netstat.c
@@ -0,0 +1,466 @@
+/*-
+ * 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";
+*/
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * netstat
+ */
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.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++ = ' ';
+ line[22] = '\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..fc12e51
--- /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/time.h>
+#include <sys/proc.h>
+#include <sys/user.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..3c27e1c
--- /dev/null
+++ b/usr.bin/systat/swap.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
+#if 0
+static char sccsid[] = "@(#)swap.c 8.3 (Berkeley) 4/29/95";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#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 *getbsize __P((int *headerlenp, long *blocksizep));
+void showspace __P((char *header, int hlen, long blocksize));
+
+kvm_t *kd;
+
+struct nlist syms[] = {
+ { "_swaplist" },/* list of free swap areas */
+#define VM_SWAPLIST 0
+ { "_swdevt" }, /* list of swap devices and sizes */
+#define VM_SWDEVT 1
+ { "_nswap" }, /* size of largest swap device */
+#define VM_NSWAP 2
+ { "_nswdev" }, /* number of swap devices */
+#define VM_NSWDEV 3
+ { "_dmmax" }, /* maximum size of a swap block */
+#define VM_DMMAX 4
+ 0
+};
+
+static int nswap, nswdev, dmmax;
+static struct swdevt *sw;
+static long *perdev, blocksize;
+static int nfree, hlen;
+static struct rlisthdr swaplist;
+
+#define SVAR(var) __STRING(var) /* to force expansion */
+#define KGET(idx, var) \
+ KGET1(idx, &var, sizeof(var), SVAR(var), (0))
+#define KGET1(idx, p, s, msg, rv) \
+ KGET2(syms[idx].n_value, p, s, msg, rv)
+#define KGET2(addr, p, s, msg, rv) \
+ if (kvm_read(kd, addr, p, s) != s) { \
+ error("cannot read %s: %s", msg, kvm_geterr(kd)); \
+ return rv; \
+ }
+
+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);
+}
+
+/*
+ * The meat of all the swap stuff is stolen from pstat(8)'s
+ * swapmode(), which is based on a program called swapinfo written by
+ * Kevin Lahey <kml@rokkaku.atl.ga.us>.
+ */
+
+initswap()
+{
+ int i;
+ char msgbuf[BUFSIZ];
+ static int once = 0;
+ u_long ptr;
+
+ 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);
+ if ((sw = malloc(nswdev * sizeof(*sw))) == NULL ||
+ (perdev = malloc(nswdev * sizeof(*perdev))) == NULL)
+ err(1, "malloc");
+ KGET1(VM_SWDEVT, &ptr, sizeof ptr, "swdevt", (0));
+ KGET2(ptr, sw, nswdev * sizeof(*sw), "*swdevt", (0));
+ once = 1;
+ return (1);
+}
+
+void
+fetchswap()
+{
+ struct rlist head;
+ struct rlist *swapptr;
+
+ /* Count up swap space. */
+ nfree = 0;
+ memset(perdev, 0, nswdev * sizeof(*perdev));
+ KGET1(VM_SWAPLIST, &swaplist, sizeof swaplist, "swaplist", /* none */);
+ swapptr = swaplist.rlh_list;
+ while (swapptr) {
+ int top, bottom, next_block;
+
+ KGET2((unsigned long)swapptr, &head,
+ sizeof(struct rlist), "swapptr", /* none */);
+
+ top = head.rl_end;
+ bottom = head.rl_start;
+
+ nfree += top - bottom + 1;
+
+ /*
+ * Swap space is split up among the configured disks.
+ *
+ * For interleaved swap devices, the first dmmax blocks
+ * of swap space some from the first disk, the next dmmax
+ * blocks from the next, and so on up to nswap blocks.
+ *
+ * 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.
+ */
+ while (top / dmmax != bottom / dmmax) {
+ next_block = ((bottom + dmmax) / dmmax);
+ perdev[(bottom / dmmax) % nswdev] +=
+ next_block * dmmax - bottom;
+ bottom = next_block * dmmax;
+ }
+ perdev[(bottom / dmmax) % nswdev] +=
+ top - bottom + 1;
+
+ swapptr = head.rl_next;
+ }
+
+}
+
+void
+labelswap()
+{
+ char *header, *p;
+ 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++) {
+ if (!sw[i].sw_freed)
+ continue;
+ p = devname(sw[i].sw_dev, S_IFBLK);
+ mvwprintw(wnd, i + 1, 0, "%-5s",
+ sw[i].sw_dev == NODEV ? "[NFS]" :
+ p == NULL ? "??" : p);
+ }
+}
+
+void
+showswap()
+{
+ int col, row, div, i, j, k, avail, npfree, used, xsize, xfree;
+
+ div = blocksize / 512;
+ avail = npfree = 0;
+ for (i = k = 0; i < nswdev; i++) {
+ /*
+ * Don't report statistics for partitions which have not
+ * yet been activated via swapon(8).
+ */
+ if (!sw[i].sw_freed)
+ continue;
+
+ col = 5;
+ mvwprintw(wnd, i + 1, col, "%*d", hlen, sw[i].sw_nblks / div);
+ col += hlen;
+
+ /*
+ * The first dmmax is never allocated to avoid trashing of
+ * disklabels
+ */
+ xsize = sw[i].sw_nblks - dmmax;
+ 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;
+ k++;
+ }
+ /*
+ * 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, k + 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..a487024
--- /dev/null
+++ b/usr.bin/systat/systat.1
@@ -0,0 +1,426 @@
+.\" 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 8 ) ,
+virtual memory statistics (a la
+.Xr vmstat 8 ) ,
+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 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
+Below the process display 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
+At the bottom left 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 eight disks
+(this is controlled by the constant DK_NDRIVE in /sys/dkstat.h as
+a kernel compile-time constant).
+.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..1436234
--- /dev/null
+++ b/usr.bin/systat/vmstat.c
@@ -0,0 +1,680 @@
+/*-
+ * 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/proc.h>
+#include <sys/uio.h>
+#include <sys/namei.h>
+#include <sys/sysctl.h>
+#include <sys/vmmeter.h>
+
+#include <vm/vm_param.h>
+
+#include <signal.h>
+#include <nlist.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <paths.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.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 46
+#define INTSROW 6 /* 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;
+ struct tm *tp;
+
+ time(&now);
+ tp = localtime(&now);
+ (void) strftime(buf, sizeof(buf), "%c", tp);
+ 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 + 41, "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 Dir-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,
+ " %4.4s", 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, 8);
+ putint(pgtokb(total.t_armshr), MEMROW + 2, MEMCOL + 11, 8);
+ putint(pgtokb(total.t_avm), MEMROW + 2, MEMCOL + 19, 9);
+ putint(pgtokb(total.t_avmshr), MEMROW + 2, MEMCOL + 28, 9);
+ putint(pgtokb(total.t_rm), MEMROW + 3, MEMCOL + 3, 8);
+ putint(pgtokb(total.t_rmshr), MEMROW + 3, MEMCOL + 11, 8);
+ putint(pgtokb(total.t_vm), MEMROW + 3, MEMCOL + 19, 9);
+ putint(pgtokb(total.t_vmshr), MEMROW + 3, MEMCOL + 28, 9);
+ putint(pgtokb(total.t_free), MEMROW + 2, MEMCOL + 37, 8);
+ 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,
+ " %4.4s", 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 + 33, 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 *) calloc(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..f9a8bdf
--- /dev/null
+++ b/usr.bin/tail/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/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 *));
+
+int bytes __P((FILE *, off_t));
+int lines __P((FILE *, off_t));
+
+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..b9457af
--- /dev/null
+++ b/usr.bin/tail/forward.c
@@ -0,0 +1,236 @@
+/*-
+ * 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 <err.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;
+
+ 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
+ if (bytes(fp, off))
+ return;
+ 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
+ if (lines(fp, off))
+ return;
+ break;
+ }
+
+ /*
+ * We pause for one second after displaying any data that has
+ * accumulated since we read the file.
+ */
+
+ 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. */
+ second.tv_sec = 1;
+ second.tv_usec = 0;
+ if (select(0, NULL, NULL, NULL, &second) == -1)
+ if (errno != EINTR)
+ err(1, "select");
+ 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) {
+ errno = EFBIG;
+ ierr();
+ return;
+ }
+
+ if ((start = mmap(NULL, (size_t)size,
+ PROT_READ, MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) {
+ ierr();
+ 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)) {
+ ierr();
+ return;
+ }
+}
diff --git a/usr.bin/tail/misc.c b/usr.bin/tail/misc.c
new file mode 100644
index 0000000..8891819
--- /dev/null
+++ b/usr.bin/tail/misc.c
@@ -0,0 +1,62 @@
+/*-
+ * 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 <err.h>
+#include "extern.h"
+
+void
+ierr()
+{
+ warn("%s", fname);
+ rval = 1;
+}
+
+void
+oerr()
+{
+ err(1, "stdout");
+}
diff --git a/usr.bin/tail/read.c b/usr.bin/tail/read.c
new file mode 100644
index 0000000..feecf3c
--- /dev/null
+++ b/usr.bin/tail/read.c
@@ -0,0 +1,201 @@
+/*-
+ * 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 <err.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.
+ */
+int
+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, "malloc");
+
+ for (wrap = 0, ep = p + off; (ch = getc(fp)) != EOF;) {
+ *p = ch;
+ if (++p == ep) {
+ wrap = 1;
+ p = sp;
+ }
+ }
+ if (ferror(fp)) {
+ ierr();
+ return 1;
+ }
+
+ 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);
+ }
+ return 0;
+}
+
+/*
+ * 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.
+ */
+int
+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, "malloc");
+ 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, "realloc");
+ 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, "realloc");
+ }
+ 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 1;
+ }
+ 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);
+ }
+ return 0;
+}
diff --git a/usr.bin/tail/reverse.c b/usr.bin/tail/reverse.c
new file mode 100644
index 0000000..0a07f4f
--- /dev/null
+++ b/usr.bin/tail/reverse.c
@@ -0,0 +1,265 @@
+/*-
+ * 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 <err.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) {
+ errno = EFBIG;
+ ierr();
+ return;
+ }
+
+ if ((start = mmap(NULL, (size_t)size,
+ PROT_READ, MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) {
+ ierr();
+ 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))
+ ierr();
+}
+
+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, "malloc");
+ 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 (ferror(fp)) {
+ ierr();
+ return;
+ }
+
+ /*
+ * 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) {
+ warnx("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..4c7ea6f
--- /dev/null
+++ b/usr.bin/tail/tail.c
@@ -0,0 +1,303 @@
+/*-
+ * 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 <err.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) \
+ errx(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")) != -1)
+ 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)
+ errx(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, "malloc");
+ *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:
+ errx(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..921a12e
--- /dev/null
+++ b/usr.bin/talk/Makefile
@@ -0,0 +1,11 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id$
+
+PROG= talk
+DPADD= ${LIBCURSES} ${LIBTERMCAP}
+LDADD= -lcurses -ltermcap
+CFLAGS+= -Wall -Wstrict-prototypes -Wno-unused
+SRCS= ctl.c ctl_transact.c display.c get_addrs.c get_iface.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..2f77608
--- /dev/null
+++ b/usr.bin/talk/ctl.c
@@ -0,0 +1,116 @@
+/*
+ * 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;
+
+void
+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 */
+void
+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 */
+void
+print_addr(addr)
+ struct sockaddr_in addr;
+{
+ int i;
+
+ printf("addr = %lx, port = %o, family = %o zero = ",
+ addr.sin_addr.s_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..2cd4dce
--- /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 <errno.h>
+#include <string.h>
+#include "talk.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
+ */
+void
+ctl_transact(target, msg, type, rp)
+ struct in_addr target;
+ CTL_MSG msg;
+ int type;
+ CTL_RESPONSE *rp;
+{
+ fd_set read_mask, ctl_mask;
+ int nready = 0, cc;
+ struct timeval wait;
+
+ msg.type = type;
+ daemon_addr.sin_addr = target;
+ daemon_addr.sin_port = daemon_port;
+ FD_ZERO(&ctl_mask);
+ FD_SET(ctl_sockt, &ctl_mask);
+
+ /*
+ * 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..7b43d46
--- /dev/null
+++ b/usr.bin/talk/display.c
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+int
+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.
+ */
+void
+display(win, text, size)
+ register xwin_t *win;
+ register 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') {
+ write(STDOUT_FILENO, text, 1);
+ text++;
+ continue;
+ }
+ if (!isprint((unsigned char)*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, (unsigned char)*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
+ */
+int
+readwin(win, line, col)
+ WINDOW *win;
+ int line;
+ int col;
+{
+ 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..7ac9267
--- /dev/null
+++ b/usr.bin/talk/get_addrs.c
@@ -0,0 +1,72 @@
+/*
+ * 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 <string.h>
+#include <netdb.h>
+#include <stdio.h>
+#include "talk.h"
+#include "talk_ctl.h"
+
+void
+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());
+
+ 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);
+ if (get_iface(&his_machine_addr, &my_machine_addr) == -1) {
+ perror("failed to find my interface address");
+ exit(-1);
+ }
+ /* 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_iface.c b/usr.bin/talk/get_iface.c
new file mode 100644
index 0000000..3aef9b2
--- /dev/null
+++ b/usr.bin/talk/get_iface.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1994, 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.
+ *
+ * From:
+ * Id: find_interface.c,v 1.1 1995/08/14 16:08:39 wollman Exp
+ *
+ * $Id$
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "talk.h"
+
+/*
+ * Try to find the interface address that is used to route an IP
+ * packet to a remote peer.
+ */
+
+int
+get_iface(dst, iface)
+ struct in_addr *dst;
+ struct in_addr *iface;
+{
+ static struct sockaddr_in local;
+ struct sockaddr_in remote;
+ struct hostent *hp;
+ int s, rv, namelen;
+
+ memcpy(&remote.sin_addr, dst, sizeof remote.sin_addr);
+ remote.sin_port = htons(60000);
+ remote.sin_family = AF_INET;
+ remote.sin_len = sizeof remote;
+
+ local.sin_addr.s_addr = htonl(INADDR_ANY);
+ local.sin_port = htons(60000);
+ local.sin_family = AF_INET;
+ local.sin_len = sizeof local;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ return -1;
+
+ do {
+ rv = bind(s, (struct sockaddr *)&local, sizeof local);
+ local.sin_port = htons(ntohs(local.sin_port) + 1);
+ } while(rv < 0 && errno == EADDRINUSE);
+
+ if (rv < 0) {
+ close(s);
+ return -1;
+ }
+
+ do {
+ rv = connect(s, (struct sockaddr *)&remote, sizeof remote);
+ remote.sin_port = htons(ntohs(remote.sin_port) + 1);
+ } while(rv < 0 && errno == EADDRINUSE);
+
+ if (rv < 0) {
+ close(s);
+ return -1;
+ }
+
+ namelen = sizeof local;
+ rv = getsockname(s, (struct sockaddr *)&local, &namelen);
+ close(s);
+ if (rv < 0)
+ return -1;
+
+ memcpy(iface, &local.sin_addr, sizeof local.sin_addr);
+ return 0;
+}
diff --git a/usr.bin/talk/get_names.c b/usr.bin/talk/get_names.c
new file mode 100644
index 0000000..793ce72
--- /dev/null
+++ b/usr.bin/talk/get_names.c
@@ -0,0 +1,116 @@
+/*
+ * 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 <unistd.h>
+#include <string.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include "talk.h"
+
+extern CTL_MSG msg;
+
+/*
+ * Determine the local and remote user, tty, and machines
+ */
+void
+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..7eded28
--- /dev/null
+++ b/usr.bin/talk/init_disp.c
@@ -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.
+ */
+
+#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/types.h>
+#include <sys/stat.h>
+#include <sys/termios.h>
+#include <sys/ttydefaults.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.
+ */
+void
+init_display()
+{
+ struct sigaction sa;
+
+ if (initscr() == NULL)
+ errx(1, "Terminal type unset or lacking necessary features.");
+ (void) sigaction(SIGTSTP, (struct sigaction *)0, &sa);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ (void) sigaction(SIGTSTP, &sa, (struct sigaction *)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);
+ idlok(my_win.x_win, TRUE);
+ 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);
+ idlok(my_win.x_win, TRUE);
+ scrollok(his_win.x_win, TRUE);
+ wclear(his_win.x_win);
+
+ line_win = newwin(1, COLS, my_win.x_nlines, 0);
+#if defined(hline) || defined(whline) || defined(NCURSES_VERSION)
+ whline(line_win, 0, COLS);
+#else
+ box(line_win, '-', '-');
+#endif
+ 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.
+ */
+void
+set_edit_chars()
+{
+ char buf[3];
+ int cc;
+ struct termios tio;
+
+ tcgetattr(0, &tio);
+ my_win.cerase = tio.c_cc[VERASE];
+ my_win.kill = tio.c_cc[VKILL];
+ my_win.werase = tio.c_cc[VWERASE];
+ if (my_win.cerase == (char)_POSIX_VDISABLE)
+ my_win.kill = CERASE;
+ if (my_win.kill == (char)_POSIX_VDISABLE)
+ my_win.kill = CKILL;
+ if (my_win.werase == (char)_POSIX_VDISABLE)
+ my_win.werase = CWERASE;
+ 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];
+}
+
+/* ARGSUSED */
+void
+sig_sent(signo)
+ int signo;
+{
+
+ message("Connection closing. Exiting");
+ quit();
+}
+
+/*
+ * All done talking...hang up the phone and reset terminal thingy's
+ */
+void
+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..579aed0
--- /dev/null
+++ b/usr.bin/talk/invite.c
@@ -0,0 +1,194 @@
+/*
+ * 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;
+jmp_buf invitebuf;
+
+void
+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
+ */
+/* ARGSUSED */
+void
+re_invite(signo)
+ int signo;
+{
+
+ 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
+ */
+void
+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
+ */
+void
+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..7b8ead8
--- /dev/null
+++ b/usr.bin/talk/io.c
@@ -0,0 +1,145 @@
+/*
+ * 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
+
+/*
+ * The routine to do the actual talking
+ */
+void
+talk()
+{
+ int nb;
+ fd_set read_set, read_template;
+ char buf[BUFSIZ];
+ struct timeval wait;
+
+ message("Connection established");
+ write(STDOUT_FILENO, "\007\007\007", 3);
+
+ current_line = 0;
+
+ /*
+ * Wait on both the other process (sockt_mask) and
+ * standard input ( STDIN_MASK )
+ */
+ FD_ZERO(&read_template);
+ FD_SET(sockt, &read_template);
+ FD_SET(fileno(stdin), &read_template);
+ 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 (FD_ISSET(sockt, &read_set)) {
+ /* 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 (FD_ISSET(fileno(stdin), &read_set)) {
+ /*
+ * 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)
+ */
+void
+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
+ */
+void
+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..62356ea
--- /dev/null
+++ b/usr.bin/talk/look_up.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[] = "@(#)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.
+ */
+int
+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*/
+ return (0);
+}
+
+/*
+ * Look for an invitation on 'machine'
+ */
+int
+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..0b3eeee
--- /dev/null
+++ b/usr.bin/talk/msgs.c
@@ -0,0 +1,82 @@
+/*
+ * 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;
+
+/* ARGSUSED */
+void
+disp_msg(signo)
+ int signo;
+{
+ message(current_state);
+}
+
+void
+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);
+}
+
+void
+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..68b9d57
--- /dev/null
+++ b/usr.bin/talk/talk.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 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"
+#include <locale.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
+ */
+
+int main __P((int, char **));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ (void) setlocale(LC_CTYPE, "");
+
+ 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();
+ return 0;
+}
diff --git a/usr.bin/talk/talk.h b/usr.bin/talk/talk.h
new file mode 100644
index 0000000..63bd373
--- /dev/null
+++ b/usr.bin/talk/talk.h
@@ -0,0 +1,94 @@
+/*
+ * 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 <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <protocols/talkd.h>
+#include <curses.h>
+#include <unistd.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;
+
+extern void announce_invite __P((void));
+extern int check_local __P((void));
+extern void check_writeable __P((void));
+extern void ctl_transact __P((struct in_addr,CTL_MSG,int,CTL_RESPONSE *));
+extern void disp_msg __P((int));
+extern void display __P((xwin_t *, char *, int));
+extern void end_msgs __P((void));
+extern void get_addrs __P((char *, char *));
+extern int get_iface __P((struct in_addr *, struct in_addr *));
+extern void get_names __P((int, char **));
+extern void init_display __P((void));
+extern void invite_remote __P((void));
+extern int look_for_invite __P((CTL_RESPONSE *));
+extern int max __P((int, int));
+extern void message __P((char *));
+extern void open_ctl __P((void));
+extern void open_sockt __P((void));
+extern void p_error __P((char *));
+extern void print_addr __P((struct sockaddr_in));
+extern void quit __P((void));
+extern int readwin __P((WINDOW *, int, int));
+extern void re_invite __P((int));
+extern void send_delete __P((void));
+extern void set_edit_chars __P((void));
+extern void sig_sent __P((int));
+extern void start_msgs __P((void));
+extern void talk __P((void));
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/tclsh/Makefile b/usr.bin/tclsh/Makefile
new file mode 100644
index 0000000..db56a03
--- /dev/null
+++ b/usr.bin/tclsh/Makefile
@@ -0,0 +1,20 @@
+# $Id$
+
+PROG= tclsh
+SRCS= tclAppInit.c
+
+BINDIR= /usr/bin
+
+.PATH: ${DESTDIR}/usr/libdata/tcl
+
+MAN1= tclsh.1
+
+CLEANFILES= ${MAN1}
+
+tclsh.1: ${.CURDIR}/../../contrib/tcl/doc/tclsh.1
+ sed '/\.so *man.macros/s;.*;.so /usr/share/tmac/tcl.macros;' < $> > $@
+
+LDADD+= -ltcl -lm
+DPADD+= ${LIBTCL} ${LIBM}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tconv/Makefile b/usr.bin/tconv/Makefile
new file mode 100644
index 0000000..0a4af30
--- /dev/null
+++ b/usr.bin/tconv/Makefile
@@ -0,0 +1,12 @@
+# Makefile for tconv
+# $Id$
+
+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..d7e1224
--- /dev/null
+++ b/usr.bin/tconv/tconv.1
@@ -0,0 +1,179 @@
+.\" @(#) mytinfo tconv.1 3.2 92/02/01 public domain, By Ross Ridge
+.\" $Id$
+.\"
+.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
+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"
+termcap(3),
+curses(3),
+term(5),
+termcap(5),
+terminfo(5).
+.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 canceled
+capabilities marked as canceled, 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..3508372
--- /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;
+u_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")) != -1)
+ 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 %lu records: %lu bytes\n",
+ filen, record, size);
+ needeof = 1;
+ filen++;
+ tsize += size;
+ size = record = lastrec = 0;
+ lastnread = 0;
+ }
+ lastnread = nread;
+ }
+ fprintf(msg, "total length: %lu 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..1008973
--- /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")) != -1)
+ 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..a7a1f02
--- /dev/null
+++ b/usr.bin/telnet/Makefile
@@ -0,0 +1,61 @@
+#
+# 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+=-DSKEY
+CFLAGS+=-I${.CURDIR}/../../lib
+
+#CFLAGS+= -DKRB4
+
+DPADD= ${LIBTERMCAP} ${LIBTELNET}
+LDADD= -ltermcap -ltelnet
+#DPADD+= ${LIBKRB} ${LIBDES}
+#LDADD+= -lkrb -ldes
+
+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..37b588f
--- /dev/null
+++ b/usr.bin/telnet/README
@@ -0,0 +1,743 @@
+
+This is a distribution of both client and server telnet. These programs
+have been compiled on:
+ telnet telnetd
+ 4.4 BSD-Lite x x
+ 4.3 BSD Reno X X
+ UNICOS 9.1 X X
+ UNICOS 9.0 X X
+ UNICOS 8.0 X X
+ BSDI 2.0 X X
+ Solaris 2.4 x x (no linemode in server)
+ SunOs 4.1.4 X X (no linemode in server)
+ Ultrix 4.3 X X (no linemode in server)
+ Ultrix 4.1 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
+ Next1.0 X X
+ UNICOS 8.3 X X
+ UNICOS 7.C X X
+ UNICOS 7.0 X X
+ SunOs 4.0.3c X X (no linemode in server)
+ 4.3 BSD X X (no linemode in server)
+ DYNIX V3.0.12 X X (no linemode in server)
+ Ultrix 3.1 X X (no linemode in server)
+ Ultrix 4.0 X X (no linemode in server)
+ SunOs 3.5 X X (no linemode in server)
+ SunOs 4.1.3 X X (no linemode in server)
+ Solaris 2.2 x x (no linemode in server)
+ Solaris 2.3 x x (no linemode in server)
+ BSDI 1.0 X X
+ BSDI 1.1 X X
+ DYNIX V3.0.17.9 X X (no linemode in server)
+ HP-UX 8.0 x x (no linemode in server)
+
+This code should work, but there are no guarantees.
+
+May 30, 1995
+
+This release represents what is on the 4.4BSD-Lite2 release, which
+should be the final BSD release. I will continue to support of
+telnet, The code (without encryption) is available via anonymous ftp
+from ftp.cray.com, in src/telnet/telnet.YY.MM.DD.NE.tar.Z, where
+YY.MM.DD is replaced with the year, month and day of the release.
+If you can't find it at one of these places, at some point in the
+near future information about the latest releases should be available
+from ftp.borman.com.
+
+In addition, the version with the encryption code is available via
+ftp from net-dist.mit.edu, in the directory /pub/telnet. There
+is a README file there that gives further information on how
+to get the distribution.
+
+Questions, comments, bug reports and bug fixes can be sent to
+one of these addresses:
+ dab@borman.com
+ dab@cray.com
+ dab@bsdi.com
+
+This release is mainly bug fixes and code cleanup.
+
+ Replace all calls to bcopy()/bzero() with calls to
+ memmove()/memset() and all calls to index()/rindex()
+ with calls to strchr()/strrchr().
+
+ Add some missing diagnostics for option tracing
+ to telnetd.
+
+ Add support for BSDI 2.0 and Solaris 2.4.
+
+ Add support for UNICOS 8.0
+
+ Get rid of expanded tabs and trailing white spaces.
+
+ From Paul Vixie:
+ Fix for telnet going into an endless spin
+ when the session dies abnormally.
+
+ From Jef Poskanzer:
+ Changes to allow telnet to compile
+ under SunOS 3.5.
+
+ From Philip Guenther:
+ makeutx() doesn't expand utmpx,
+ use pututxline() instead.
+
+ From Chris Torek:
+ Add a sleep(1) before execing login
+ to avoid race condition that can eat
+ up the login prompt.
+ Use terminal speed directly if it is
+ not an encoded value.
+
+ From Steve Parker:
+ Fix to realloc() call. Fix for execing
+ login on solaris with no user name.
+
+January 19, 1994
+
+This is a list of some of the changes since the last tar release
+of telnet/telnetd. There are probably other changes that aren't
+listed here, but this should hit a lot of the main ones.
+
+ General:
+ Changed #define for AUTHENTICATE to AUTHENTICATION
+ Changed #define for ENCRYPT to ENCRYPTION
+ Changed #define for DES_ENCRYPT to DES_ENCRYPTION
+
+ Added support for SPX authentication: -DSPX
+
+ Added support for Kerberos Version 5 authentication: -DKRB5
+
+ Added support for ANSI C function prototypes
+
+ Added support for the NEW-ENVIRON option (RFC-1572)
+ including support for USERVAR.
+
+ Made support for the old Environment Option (RFC-1408)
+ conditional on -DOLD_ENVIRON
+
+ Added #define ENV_HACK - support for RFC 1571
+
+ The encryption code is removed from the public distributions.
+ Domestic 4.4 BSD distributions contain the encryption code.
+
+ ENV_HACK: Code to deal with systems that only implement
+ the old ENVIRON option, and have reversed definitions
+ of ENV_VAR and ENV_VAL. Also fixes ENV processing in
+ client to handle things besides just the default set...
+
+ NO_BSD_SETJMP: UNICOS configuration for
+ UNICOS 6.1/6.0/5.1/5.0 systems.
+
+ STREAMSPTY: Use /dev/ptmx to get a clean pty. This
+ is for SVr4 derivatives (Like Solaris)
+
+ UTMPX: For systems that have /etc/utmpx. This is for
+ SVr4 derivatives (Like Solaris)
+
+ Definitions for BSDI 1.0
+
+ Definitions for 4.3 Reno and 4.4 BSD.
+
+ Definitions for UNICOS 8.0 and UNICOS 7.C
+
+ Definitions for Solaris 2.0
+
+ Definitions for HP-UX 8.0
+
+ Latest Copyright notices from Berkeley.
+
+ FLOW-CONTROL: support for RFC-XXXx
+
+
+ Client Specific:
+
+ Fix the "send" command to not send garbage...
+
+ Fix status message for "skiprc"
+
+ Make sure to send NAWS after telnet has been suspended
+ or an external command has been run, if the window size
+ has changed.
+
+ sysV88 support.
+
+ Server Specific:
+
+ Support flowcontrol option in non-linemode servers.
+
+ -k Server supports Kludge Linemode, but will default to
+ either single character mode or real Linemode support.
+ The user will have to explicitly ask to switch into
+ kludge linemode. ("stty extproc", or escape back to
+ to telnet and say "mode line".)
+
+ -u Specify the length of the hostname field in the utmp
+ file. Hostname longer than this length will be put
+ into the utmp file in dotted decimal notation, rather
+ than putting in a truncated hostname.
+
+ -U Registered hosts only. If a reverse hostname lookup
+ fails, the connection will be refused.
+
+ -f/-F
+ Allows forwarding of credentials for KRB5.
+
+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 been assigned to, but it is still
+ subject to change. 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", "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", and "help".
+
+ The LOGOUT option is now supported by both telnet
+ and telnetd, a new command, "logout", was added
+ to support this.
+
+ Several new toggle options were added:
+ "autoencrypt", "autodecrypt", "autologin", "authdebug",
+ "encdebug", "skiprc", "verbose_encrypt"
+
+ 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.
+
+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>
+
+libtelnet/Makefile.4.4:
+telnet/Makefile.4.4:
+telnetd/Makefile.4.4:
+ These are the makefiles that can be used on a 4.3Reno
+ system when this software is installed in /usr/src/lib/libtelnet,
+ /usr/src/libexec/telnetd, and /usr/src/usr.bin/telnet.
+
+
+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
+
+ LOGOUT: As per RFC 727
+
+ 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.
+
+ ENCRYPTION:
+ 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..6e955de
--- /dev/null
+++ b/usr.bin/telnet/commands.c
@@ -0,0 +1,2822 @@
+/*
+ * 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];
+
+#if defined(SKEY)
+#include <sys/wait.h>
+#define PATH_SKEY "/usr/bin/key"
+ int
+skey_calc(argc, argv)
+ int argc;
+ char **argv;
+{
+ int status;
+
+ if(argc != 3) {
+ printf("%s sequence challenge\n", argv[0]);
+ return;
+ }
+
+ switch(fork()) {
+ case 0:
+ execv(PATH_SKEY, argv);
+ exit (1);
+ case -1:
+ perror("fork");
+ break;
+ default:
+ (void) wait(&status);
+ if (WIFEXITED(status))
+ return (WEXITSTATUS(status));
+ return (0);
+ }
+}
+#endif
+
+ 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, 1) < 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 != INADDR_NONE) {
+ sin.sin_addr.s_addr = temp;
+ sin.sin_family = AF_INET;
+ host = gethostbyaddr((char *)&temp, sizeof(temp), AF_INET);
+ if (host)
+ (void) strncpy(_hostname, host->h_name, sizeof(_hostname));
+ else
+ (void) strncpy(_hostname, hostp, sizeof(_hostname));
+ _hostname[sizeof(_hostname)-1] = '\0';
+ 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 */
+ memmove((caddr_t)&sin.sin_addr,
+ host->h_addr_list[0], host->h_length);
+#else /* defined(h_addr) */
+ memmove((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) */
+#if defined(SKEY)
+ skeyhelp[] = "compute response to s/key challenge",
+#endif
+ 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 },
+ { "telnet", openhelp, tn, 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 },
+#if defined(SKEY)
+ { "skey", skeyhelp, skey_calc, 0 },
+#endif
+ 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 && (strlen(rcname) + 10) < sizeof(rcbuf))
+ 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..43fdb43
--- /dev/null
+++ b/usr.bin/telnet/externs.h
@@ -0,0 +1,478 @@
+/*
+ * 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 */
+ clienteof; /* Client received EOF */
+
+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..3c4d8df
--- /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")) != -1) {
+ 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..beddb64
--- /dev/null
+++ b/usr.bin/telnet/sys_bsd.c
@@ -0,0 +1,1191 @@
+/*
+ * 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 <unistd.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: %s\r\n", strerror(errno));
+ 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 == EIO)
+ c = 0;
+ if (c < 0 && errno == EWOULDBLOCK) {
+ c = 0;
+ } else {
+ if (c < 0) {
+ return -1;
+ }
+ if (c == 0) {
+ /* must be an EOF... */
+ if (MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
+ *ttyiring.supply = termEofChar;
+ c = 1;
+ } else {
+ clienteof = 1;
+ shutdown(net, 1);
+ return 0;
+ }
+ }
+ 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..0db8a39
--- /dev/null
+++ b/usr.bin/telnet/telnet.1
@@ -0,0 +1,1368 @@
+.\" 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
+.\" $Id$
+.\"
+.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 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 SEE ALSO
+.Xr rlogin 1 ,
+.Xr rsh 1 ,
+.Xr hosts 5 ,
+.Xr nologin 5 ,
+.Xr telnetd 8
+.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..3f735a8
--- /dev/null
+++ b/usr.bin/telnet/telnet.c
@@ -0,0 +1,2560 @@
+/*
+ * 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) ((my_want_state_is_wont(TELOPT_BINARY)) ? ((x)&0x7f) : (x))
+
+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,
+ clienteof = 0;
+
+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) && (clienteof == 0) && (shell_active == 0);
+#else /* defined(TN3270) */
+ ttyin = ring_empty_count(&ttyiring) && (clienteof == 0);
+#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..aeacbb0
--- /dev/null
+++ b/usr.bin/telnet/terminal.c
@@ -0,0 +1,223 @@
+/*
+ * 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);
+ if (n1 > 0)
+ 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..a75cd1e
--- /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.2 (Berkeley) 5/30/95";
+#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) {
+ memmove(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..4763f43
--- /dev/null
+++ b/usr.bin/tftp/main.c
@@ -0,0 +1,738 @@
+/*
+ * 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 void command __P((void)) __dead2;
+
+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) ");
+ fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
+ 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) ");
+ fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
+ 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) ");
+ fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
+ 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) ");
+ fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
+ 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) ");
+ fgets(&line[strlen(line)], sizeof line - strlen(line), stdin);
+ 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 void
+command()
+{
+ register struct cmd *c;
+ char *cp;
+
+ for (;;) {
+ printf("%s> ", prompt);
+ if (fgets(line, sizeof line , stdin) == 0) {
+ if (feof(stdin)) {
+ exit(0);
+ } else {
+ continue;
+ }
+ }
+ if ((cp = strchr(line, '\n')))
+ *cp = '\0';
+ 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;
+ if ((cp = strchr(line, '\n')))
+ *cp = '\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..52b555e
--- /dev/null
+++ b/usr.bin/time/time.1
@@ -0,0 +1,97 @@
+.\" 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 ,
+.Xr getrusage 2 ,
+.Xr wait 2
+.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..52a5709
--- /dev/null
+++ b/usr.bin/time/time.c
@@ -0,0 +1,173 @@
+/*
+ * 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 <sys/sysctl.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <stdio.h>
+
+static int getstathz __P((void));
+
+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")) != -1)
+ 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 = getstathz();
+ u_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;
+
+ /*
+ * If our round-off on the tick calculation still puts us at 0,
+ * then always assume at least one tick.
+ */
+ if (ticks == 0)
+ ticks = 1;
+
+ 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 (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
+}
+
+/*
+ * Return the frequency of the kernel's statistics clock.
+ */
+static int
+getstathz()
+{
+ struct clockinfo clockrate;
+ int mib[2];
+ size_t size;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_CLOCKRATE;
+ size = sizeof clockrate;
+ if (sysctl(mib, 2, &clockrate, &size, NULL, 0) == -1)
+ err(1, "sysctl kern.clockrate");
+ return clockrate.stathz;
+}
diff --git a/usr.bin/tip/Makefile b/usr.bin/tip/Makefile
new file mode 100644
index 0000000..b8d5b38
--- /dev/null
+++ b/usr.bin/tip/Makefile
@@ -0,0 +1,5 @@
+SUBDIR=libacu tip
+
+TAGS= yes
+
+.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..4f8a1f5
--- /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 (reset_delay))
+ 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 (reset_delay);
+ }
+ 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/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/Makefile b/usr.bin/tip/tip/Makefile
new file mode 100644
index 0000000..af720f3
--- /dev/null
+++ b/usr.bin/tip/tip/Makefile
@@ -0,0 +1,25 @@
+# @(#)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(${.OBJDIR}/../libacu)
+LIBACU=${.OBJDIR}/../libacu/libacu.a
+.else
+LIBACU=${.CURDIR}/../libacu/libacu.a
+.endif
+
+PROG= tip
+DPADD= ${LIBACU} ${LIBUTIL}
+LDADD= ${LIBACU} -lutil
+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 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..52d35c7
--- /dev/null
+++ b/usr.bin/tip/tip/hunt.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[] = "@(#)hunt.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <libutil.h>
+#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;
+ int res;
+
+ f = signal(SIGALRM, dead);
+ while (cp = getremote(name)) {
+ deadfl = 0;
+ uucplock = rindex(cp, '/')+1;
+ if ((res = uu_lock(uucplock)) != UU_LOCK_OK) {
+ if (res != UU_LOCK_INUSE)
+ fprintf(stderr, "uu_lock: %s\n", uu_lockerr(res));
+ 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..f23e7d9
--- /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
+.Em 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 1
+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 1
+looks up the specified modem in
+.Pa /etc/modems.
+If a modem entry is found,
+the corresponding capabilities determine how
+.Xr tip 1
+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..687fda9
--- /dev/null
+++ b/usr.bin/tip/tip/tip.1
@@ -0,0 +1,442 @@
+.\" 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
+.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
+.Sh DESCRIPTION
+.Nm Tip
+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.
+.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 between 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 cu 1 ,
+.Xr phones 5 ,
+.Xr remote 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..b50f4c3
--- /dev/null
+++ b/usr.bin/tip/tip/tip.c
@@ -0,0 +1,688 @@
+/*
+ * 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()
+{
+ int i;
+ 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) {
+ i = getchar();
+ if (i == EOF)
+ break;
+ gch = i&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))) {
+ i = getchar();
+ if (i == EOF)
+ break;
+ gch = i & 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));
+ int i;
+
+ i = getchar();
+ if (i == EOF)
+ return 0;
+ gch = (i&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.");
+ tipabort("Something wrong...");
+ }
+}
+
+/*
+ * 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/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/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..b098041
--- /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 = (char *)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..db31b2e
--- /dev/null
+++ b/usr.bin/tn3270/mset/Makefile
@@ -0,0 +1,40 @@
+# @(#)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
+
+.if exists(${.OBJDIR}/../tools/mkastosc)
+MKOBJ=${.OBJDIR}/../tools/mkastosc
+.else
+MKOBJ=${.CURDIR}/../tools/mkastosc
+.endif
+
+astosc.o: astosc.OUT
+astosc.OUT: ${.CURDIR}/../ctlr/hostctlr.h ${.CURDIR}/../ctlr/function.h
+astosc.OUT: ${.CURDIR}/../ctlr/${KBD} ${MKOBJ}/mkastosc
+ ${MKOBJ}/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
+${MKOBJ}/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..3230487
--- /dev/null
+++ b/usr.bin/tn3270/mset/map3270.5
@@ -0,0 +1,343 @@
+.\" 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.4 (Berkeley) 6/1/94
+.\"
+.TH MAP3270 5 "June 1, 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
+.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
+.ne 1i
+ 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..128d7e5
--- /dev/null
+++ b/usr.bin/tn3270/sys_curses/termout.c
@@ -0,0 +1,948 @@
+/*-
+ * 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 <termios.h>
+#endif
+#include <stdio.h>
+#include <curses.h>
+
+#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 termios termios_info;
+ speed_t speed;
+#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)
+ (void) tcgetattr(1, &termios_info);
+ speed = cfgetospeed(&termios_info);
+ if (speed > 19200) {
+ max_changes_before_poll = 1920;
+ } else {
+ max_changes_before_poll = speed/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..df18677
--- /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.
+DPADD= ${LIBCURSES} ${LIBTERMCAP} ${LIBTELNET} ${LIBCRYPT}
+LDADD= -lcurses -ltermcap -ltelnet -lcrypt
+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(${.OBJDIR}/../tools/mkastosc)
+MKASTOSCDIR= ${.OBJDIR}/../tools/mkastosc
+.else
+MKASTOSCDIR= ${.CURDIR}/../tools/mkastosc
+.endif
+
+.if exists(${.OBJDIR}/../tools/mkastods)
+MKASTODSDIR= ${.OBJDIR}/../tools/mkastods
+.else
+MKASTODSDIR= ${.CURDIR}/../tools/mkastods
+.endif
+
+.if exists(${.OBJDIR}/../tools/mkdstoas)
+MKDSTOASDIR= ${.OBJDIR}/../tools/mkdstoas
+.else
+MKDSTOASDIR= ${.CURDIR}/../tools/mkdstoas
+.endif
+
+.if exists(${.OBJDIR}/../tools/mkhits)
+MKHITSDIR= ${.OBJDIR}/../tools/mkhits
+.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..a2aba2f
--- /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 map3270 5 ,
+.Xr termcap 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..f7c6608
--- /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}/..
+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..c06d075
--- /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}/..
+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..363eb01
--- /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}/..
+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..53ecd70
--- /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}/..
+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..169e6c4
--- /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}/..
+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/top/Makefile b/usr.bin/top/Makefile
new file mode 100644
index 0000000..7d93a45
--- /dev/null
+++ b/usr.bin/top/Makefile
@@ -0,0 +1,28 @@
+PROG= top
+
+TOPDIR= ${.CURDIR}/../../contrib/top
+.PATH: ${TOPDIR}
+
+CFLAGS+= -DHAVE_GETOPT -I${.CURDIR} -I${TOPDIR}
+
+#
+# The table size should be a prime number approximately twice as
+# large as the number of lines in /etc/passwd. The default number
+# is 20011, use /etc/make.conf to override this.
+#
+.if defined(TOP_TABLE_SIZE)
+CFLAGS+= -D"Table_size=${TOP_TABLE_SIZE}"
+.endif
+
+SRCS= commands.c display.c machine.c screen.c top.c \
+ username.c utils.c version.c
+
+DPADD= ${LIBTERMCAP} ${LIBM} ${LIBKVM}
+LDADD= -ltermcap -lm -lkvm
+BINGRP= kmem
+BINMODE=2555
+
+top.1: ${TOPDIR}/top.X top.local.1
+ cat ${.ALLSRC} > top.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/top/machine.c b/usr.bin/top/machine.c
new file mode 100644
index 0000000..57d3175
--- /dev/null
+++ b/usr.bin/top/machine.c
@@ -0,0 +1,935 @@
+/*
+ * top - a top users display for Unix
+ *
+ * SYNOPSIS: For FreeBSD-2.x system
+ *
+ * DESCRIPTION:
+ * Originally written for BSD4.4 system by Christos Zoulas.
+ * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider
+ *
+ * This is the machine-dependent module for FreeBSD 2.2
+ * Works for:
+ * FreeBSD 2.2, and probably FreeBSD 2.1.x
+ *
+ * LIBS: -lkvm
+ *
+ * AUTHOR: Christos Zoulas <christos@ee.cornell.edu>
+ * Steven Wallace <swallace@freebsd.org>
+ * Wolfram Schneider <wosch@FreeBSD.org>
+ *
+ * $Id: machine.c,v 1.2 1997/04/19 20:28:50 peter Exp $
+ */
+
+
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/param.h>
+
+#include "os.h"
+#include <stdio.h>
+#include <nlist.h>
+#include <math.h>
+#include <kvm.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/dkstat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/vmmeter.h>
+
+/* Swap */
+#include <stdlib.h>
+#include <sys/rlist.h>
+#include <sys/conf.h>
+
+#include <osreldate.h> /* for changes in kernel structures */
+
+#include "top.h"
+#include "machine.h"
+
+static int check_nlist __P((struct nlist *));
+static int getkval __P((unsigned long, int *, int, char *));
+extern char* printable __P((char *));
+int swapmode __P((int *retavail, int *retfree));
+
+
+
+/* get_process_info passes back a handle. This is what it looks like: */
+
+struct handle
+{
+ struct kinfo_proc **next_proc; /* points to next valid proc pointer */
+ int remaining; /* number of pointers remaining */
+};
+
+/* declarations for load_avg */
+#include "loadavg.h"
+
+#define PP(pp, field) ((pp)->kp_proc . field)
+#define EP(pp, field) ((pp)->kp_eproc . field)
+#define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
+
+/* define what weighted cpu is. */
+#define weighted_cpu(pct, pp) (PP((pp), p_swtime) == 0 ? 0.0 : \
+ ((pct) / (1.0 - exp(PP((pp), p_swtime) * logcpu))))
+
+/* what we consider to be process size: */
+#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
+
+/* definitions for indices in the nlist array */
+
+
+static struct nlist nlst[] = {
+#define X_CCPU 0
+ { "_ccpu" }, /* 0 */
+#define X_CP_TIME 1
+ { "_cp_time" }, /* 1 */
+#define X_HZ 2
+ { "_hz" }, /* 2 */
+#define X_STATHZ 3
+ { "_stathz" }, /* 3 */
+#define X_AVENRUN 4
+ { "_averunnable" }, /* 4 */
+
+/* Swap */
+#define VM_SWAPLIST 5
+ { "_swaplist" },/* list of free swap areas */
+#define VM_SWDEVT 6
+ { "_swdevt" }, /* list of swap devices and sizes */
+#define VM_NSWAP 7
+ { "_nswap" }, /* size of largest swap device */
+#define VM_NSWDEV 8
+ { "_nswdev" }, /* number of swap devices */
+#define VM_DMMAX 9
+ { "_dmmax" }, /* maximum size of a swap block */
+#define X_BUFSPACE 10
+ { "_bufspace" }, /* K in buffer cache */
+#define X_CNT 11
+ { "_cnt" }, /* struct vmmeter cnt */
+
+/* Last pid */
+#define X_LASTPID 12
+ { "_nextpid" },
+ { 0 }
+};
+
+/*
+ * These definitions control the format of the per-process area
+ */
+
+#ifdef P_IDLEPROC /* FreeBSD SMP kernel */
+
+static char header[] =
+ " PID X PRI NICE SIZE RES STATE C TIME WCPU CPU COMMAND";
+/* 0123456 -- field to fill in starts at header+6 */
+#define UNAME_START 6
+
+#define Proc_format \
+ "%5d %-16.16s%3d%3d%7s %6s %-6.6s%1x%7s %5.2f%% %5.2f%% %.6s"
+
+#else /* Standard kernel */
+
+static char header[] =
+ " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
+/* 0123456 -- field to fill in starts at header+6 */
+#define UNAME_START 6
+
+#define Proc_format \
+ "%5d %-16.16s%3d %3d%7s %6s %-6.6s%7s %5.2f%% %5.2f%% %.6s"
+
+#endif
+
+
+/* process state names for the "STATE" column of the display */
+/* the extra nulls in the string "run" are for adding a slash and
+ the processor number when needed */
+
+char *state_abbrev[] =
+{
+ "", "START", "RUN\0\0\0", "SLEEP", "STOP", "ZOMB",
+};
+
+
+static kvm_t *kd;
+
+/* values that we stash away in _init and use in later routines */
+
+static double logcpu;
+
+/* these are retrieved from the kernel in _init */
+
+static long hz;
+static load_avg ccpu;
+
+/* these are offsets obtained via nlist and used in the get_ functions */
+
+static unsigned long cp_time_offset;
+static unsigned long avenrun_offset;
+static unsigned long lastpid_offset;
+static long lastpid;
+static unsigned long cnt_offset;
+static unsigned long bufspace_offset;
+static long cnt;
+
+/* these are for calculating cpu state percentages */
+
+static long cp_time[CPUSTATES];
+static long cp_old[CPUSTATES];
+static long cp_diff[CPUSTATES];
+
+/* these are for detailing the process states */
+
+int process_states[6];
+char *procstatenames[] = {
+ "", " starting, ", " running, ", " sleeping, ", " stopped, ",
+ " zombie, ",
+ NULL
+};
+
+/* these are for detailing the cpu states */
+
+int cpu_states[CPUSTATES];
+char *cpustatenames[] = {
+ "user", "nice", "system", "interrupt", "idle", NULL
+};
+
+/* these are for detailing the memory statistics */
+
+int memory_stats[7];
+char *memorynames[] = {
+ "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free",
+ NULL
+};
+
+int swap_stats[7];
+char *swapnames[] = {
+/* 0 1 2 3 4 5 */
+ "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out",
+ NULL
+};
+
+
+/* these are for keeping track of the proc array */
+
+static int nproc;
+static int onproc = -1;
+static int pref_len;
+static struct kinfo_proc *pbase;
+static struct kinfo_proc **pref;
+
+/* these are for getting the memory statistics */
+
+static int pageshift; /* log base 2 of the pagesize */
+
+/* define pagetok in terms of pageshift */
+
+#define pagetok(size) ((size) << pageshift)
+
+/* useful externals */
+long percentages();
+
+int
+machine_init(statics)
+
+struct statics *statics;
+
+{
+ register int i = 0;
+ register int pagesize;
+
+ if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL)
+ return -1;
+
+
+ /* get the list of symbols we want to access in the kernel */
+ (void) kvm_nlist(kd, nlst);
+ if (nlst[0].n_type == 0)
+ {
+ fprintf(stderr, "top: nlist failed\n");
+ return(-1);
+ }
+
+ /* make sure they were all found */
+ if (i > 0 && check_nlist(nlst) > 0)
+ {
+ return(-1);
+ }
+
+ /* get the symbol values out of kmem */
+ (void) getkval(nlst[X_STATHZ].n_value, (int *)(&hz), sizeof(hz), "!");
+ if (!hz) {
+ (void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz),
+ nlst[X_HZ].n_name);
+ }
+
+ (void) getkval(nlst[X_CCPU].n_value, (int *)(&ccpu), sizeof(ccpu),
+ nlst[X_CCPU].n_name);
+
+ /* stash away certain offsets for later use */
+ cp_time_offset = nlst[X_CP_TIME].n_value;
+ avenrun_offset = nlst[X_AVENRUN].n_value;
+ lastpid_offset = nlst[X_LASTPID].n_value;
+ cnt_offset = nlst[X_CNT].n_value;
+ bufspace_offset = nlst[X_BUFSPACE].n_value;
+
+ /* this is used in calculating WCPU -- calculate it ahead of time */
+ logcpu = log(loaddouble(ccpu));
+
+ pbase = NULL;
+ pref = NULL;
+ nproc = 0;
+ onproc = -1;
+ /* get the page size with "getpagesize" and calculate pageshift from it */
+ pagesize = getpagesize();
+ pageshift = 0;
+ while (pagesize > 1)
+ {
+ pageshift++;
+ pagesize >>= 1;
+ }
+
+ /* we only need the amount of log(2)1024 for our conversion */
+ pageshift -= LOG1024;
+
+ /* fill in the statics information */
+ statics->procstate_names = procstatenames;
+ statics->cpustate_names = cpustatenames;
+ statics->memory_names = memorynames;
+ statics->swap_names = swapnames;
+
+ /* all done! */
+ return(0);
+}
+
+char *format_header(uname_field)
+
+register char *uname_field;
+
+{
+ register char *ptr;
+
+ ptr = header + UNAME_START;
+ while (*uname_field != '\0')
+ {
+ *ptr++ = *uname_field++;
+ }
+
+ return(header);
+}
+
+static int swappgsin = -1;
+static int swappgsout = -1;
+extern struct timeval timeout;
+
+void
+get_system_info(si)
+
+struct system_info *si;
+
+{
+ long total;
+ load_avg avenrun[3];
+
+ /* get the cp_time array */
+ (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
+ nlst[X_CP_TIME].n_name);
+ (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
+ nlst[X_AVENRUN].n_name);
+
+ (void) getkval(lastpid_offset, (int *)(&lastpid), sizeof(lastpid),
+ "!");
+
+ /* convert load averages to doubles */
+ {
+ register int i;
+ register double *infoloadp;
+ load_avg *avenrunp;
+
+#ifdef notyet
+ struct loadavg sysload;
+ int size;
+ getkerninfo(KINFO_LOADAVG, &sysload, &size, 0);
+#endif
+
+ infoloadp = si->load_avg;
+ avenrunp = avenrun;
+ for (i = 0; i < 3; i++)
+ {
+#ifdef notyet
+ *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
+#endif
+ *infoloadp++ = loaddouble(*avenrunp++);
+ }
+ }
+
+ /* convert cp_time counts to percentages */
+ total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
+
+ /* sum memory & swap statistics */
+ {
+ struct vmmeter sum;
+ static unsigned int swap_delay = 0;
+ static int swapavail = 0;
+ static int swapfree = 0;
+ static int bufspace = 0;
+
+ (void) getkval(cnt_offset, (int *)(&sum), sizeof(sum),
+ "_cnt");
+ (void) getkval(bufspace_offset, (int *)(&bufspace), sizeof(bufspace),
+ "_bufspace");
+
+ /* convert memory stats to Kbytes */
+ memory_stats[0] = pagetok(sum.v_active_count);
+ memory_stats[1] = pagetok(sum.v_inactive_count);
+ memory_stats[2] = pagetok(sum.v_wire_count);
+ memory_stats[3] = pagetok(sum.v_cache_count);
+ memory_stats[4] = bufspace / 1024;
+ memory_stats[5] = pagetok(sum.v_free_count);
+ memory_stats[6] = -1;
+
+ /* first interval */
+ if (swappgsin < 0) {
+ swap_stats[4] = 0;
+ swap_stats[5] = 0;
+ }
+
+ /* compute differences between old and new swap statistic */
+ else {
+ swap_stats[4] = pagetok(((sum.v_swappgsin - swappgsin)));
+ swap_stats[5] = pagetok(((sum.v_swappgsout - swappgsout)));
+ }
+
+ swappgsin = sum.v_swappgsin;
+ swappgsout = sum.v_swappgsout;
+
+ /* call CPU heavy swapmode() only for changes */
+ if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) {
+ swap_stats[3] = swapmode(&swapavail, &swapfree);
+ swap_stats[0] = swapavail;
+ swap_stats[1] = swapavail - swapfree;
+ swap_stats[2] = swapfree;
+ }
+ swap_delay = 1;
+ swap_stats[6] = -1;
+ }
+
+ /* set arrays and strings */
+ si->cpustates = cpu_states;
+ si->memory = memory_stats;
+ si->swap = swap_stats;
+
+
+ if(lastpid > 0) {
+ si->last_pid = lastpid;
+ } else {
+ si->last_pid = -1;
+ }
+}
+
+static struct handle handle;
+
+caddr_t get_process_info(si, sel, compare)
+
+struct system_info *si;
+struct process_select *sel;
+int (*compare)();
+
+{
+ register int i;
+ register int total_procs;
+ register int active_procs;
+ register struct kinfo_proc **prefp;
+ register struct kinfo_proc *pp;
+
+ /* these are copied out of sel for speed */
+ int show_idle;
+ int show_system;
+ int show_uid;
+ int show_command;
+
+
+ pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
+ if (nproc > onproc)
+ pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
+ * (onproc = nproc));
+ if (pref == NULL || pbase == NULL) {
+ (void) fprintf(stderr, "top: Out of memory.\n");
+ quit(23);
+ }
+ /* get a pointer to the states summary array */
+ si->procstates = process_states;
+
+ /* set up flags which define what we are going to select */
+ show_idle = sel->idle;
+ show_system = sel->system;
+ show_uid = sel->uid != -1;
+ show_command = sel->command != NULL;
+
+ /* count up process states and get pointers to interesting procs */
+ total_procs = 0;
+ active_procs = 0;
+ memset((char *)process_states, 0, sizeof(process_states));
+ prefp = pref;
+ for (pp = pbase, i = 0; i < nproc; pp++, i++)
+ {
+ /*
+ * Place pointers to each valid proc structure in pref[].
+ * Process slots that are actually in use have a non-zero
+ * status field. Processes with P_SYSTEM set are system
+ * processes---these get ignored unless show_sysprocs is set.
+ */
+ if (PP(pp, p_stat) != 0 &&
+ (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0)))
+ {
+ total_procs++;
+ process_states[(unsigned char) PP(pp, p_stat)]++;
+ if ((PP(pp, p_stat) != SZOMB) &&
+ (show_idle || (PP(pp, p_pctcpu) != 0) ||
+ (PP(pp, p_stat) == SRUN)) &&
+ (!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid))
+ {
+ *prefp++ = pp;
+ active_procs++;
+ }
+ }
+ }
+
+ /* if requested, sort the "interesting" processes */
+ if (compare != NULL)
+ {
+ qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare);
+ }
+
+ /* remember active and total counts */
+ si->p_total = total_procs;
+ si->p_active = pref_len = active_procs;
+
+ /* pass back a handle */
+ handle.next_proc = pref;
+ handle.remaining = active_procs;
+ return((caddr_t)&handle);
+}
+
+char fmt[128]; /* static area where result is built */
+
+char *format_next_process(handle, get_userid)
+
+caddr_t handle;
+char *(*get_userid)();
+
+{
+ register struct kinfo_proc *pp;
+ register long cputime;
+ register double pct;
+ struct handle *hp;
+ char status[16];
+
+ /* find and remember the next proc structure */
+ hp = (struct handle *)handle;
+ pp = *(hp->next_proc++);
+ hp->remaining--;
+
+
+ /* get the process's user struct and set cputime */
+ if ((PP(pp, p_flag) & P_INMEM) == 0) {
+ /*
+ * Print swapped processes as <pname>
+ */
+ char *comm = PP(pp, p_comm);
+#define COMSIZ sizeof(PP(pp, p_comm))
+ char buf[COMSIZ];
+ (void) strncpy(buf, comm, COMSIZ);
+ comm[0] = '<';
+ (void) strncpy(&comm[1], buf, COMSIZ - 2);
+ comm[COMSIZ - 2] = '\0';
+ (void) strncat(comm, ">", COMSIZ - 1);
+ comm[COMSIZ - 1] = '\0';
+ }
+
+#if 0
+ /* This does not produce the correct results */
+ cputime = PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks);
+#endif
+ cputime = PP(pp, p_rtime).tv_sec; /* This does not count interrupts */
+
+ /* calculate the base for cpu percentages */
+ pct = pctdouble(PP(pp, p_pctcpu));
+
+ /* generate "STATE" field */
+ switch (PP(pp, p_stat)) {
+ case SRUN:
+#ifdef P_IDLEPROC /* FreeBSD SMP kernel */
+ if (PP(pp, p_oncpu) >= 0)
+ sprintf(status, "CPU%d", PP(pp, p_oncpu));
+ else
+#endif
+ strcpy(status, "RUN");
+ break;
+ case SSLEEP:
+ if (PP(pp, p_wmesg) != NULL) {
+ sprintf(status, "%.6s", EP(pp, e_wmesg));
+ break;
+ }
+ /* fall through */
+ default:
+ sprintf(status, "%.6s", state_abbrev[(unsigned char) PP(pp, p_stat)]);
+ break;
+ }
+
+ /* format this entry */
+ sprintf(fmt,
+ Proc_format,
+ PP(pp, p_pid),
+ (*get_userid)(EP(pp, e_pcred.p_ruid)),
+ PP(pp, p_priority) - PZERO,
+ PP(pp, p_nice) - NZERO,
+ format_k2(pagetok(PROCSIZE(pp))),
+ format_k2(pagetok(VP(pp, vm_rssize))),
+ status,
+#ifdef P_IDLEPROC /* FreeBSD SMP kernel */
+ PP(pp, p_lastcpu),
+#endif
+ format_time(cputime),
+ 10000.0 * weighted_cpu(pct, pp) / hz,
+ 10000.0 * pct / hz,
+ printable(PP(pp, p_comm)));
+
+ /* return the result */
+ return(fmt);
+}
+
+
+/*
+ * check_nlist(nlst) - checks the nlist to see if any symbols were not
+ * found. For every symbol that was not found, a one-line
+ * message is printed to stderr. The routine returns the
+ * number of symbols NOT found.
+ */
+
+static int check_nlist(nlst)
+
+register struct nlist *nlst;
+
+{
+ register int i;
+
+ /* check to see if we got ALL the symbols we requested */
+ /* this will write one line to stderr for every symbol not found */
+
+ i = 0;
+ while (nlst->n_name != NULL)
+ {
+ if (nlst->n_type == 0)
+ {
+ /* this one wasn't found */
+ (void) fprintf(stderr, "kernel: no symbol named `%s'\n",
+ nlst->n_name);
+ i = 1;
+ }
+ nlst++;
+ }
+
+ return(i);
+}
+
+
+/*
+ * getkval(offset, ptr, size, refstr) - get a value out of the kernel.
+ * "offset" is the byte offset into the kernel for the desired value,
+ * "ptr" points to a buffer into which the value is retrieved,
+ * "size" is the size of the buffer (and the object to retrieve),
+ * "refstr" is a reference string used when printing error meessages,
+ * if "refstr" starts with a '!', then a failure on read will not
+ * be fatal (this may seem like a silly way to do things, but I
+ * really didn't want the overhead of another argument).
+ *
+ */
+
+static int getkval(offset, ptr, size, refstr)
+
+unsigned long offset;
+int *ptr;
+int size;
+char *refstr;
+
+{
+ if (kvm_read(kd, offset, (char *) ptr, size) != size)
+ {
+ if (*refstr == '!')
+ {
+ return(0);
+ }
+ else
+ {
+ fprintf(stderr, "top: kvm_read for %s: %s\n",
+ refstr, strerror(errno));
+ quit(23);
+ }
+ }
+ return(1);
+}
+
+/* comparison routine for qsort */
+
+/*
+ * proc_compare - comparison function for "qsort"
+ * Compares the resource consumption of two processes using five
+ * distinct keys. The keys (in descending order of importance) are:
+ * percent cpu, cpu ticks, state, resident set size, total virtual
+ * memory usage. The process states are ordered as follows (from least
+ * to most important): WAIT, zombie, sleep, stop, start, run. The
+ * array declaration below maps a process state index into a number
+ * that reflects this ordering.
+ */
+
+static unsigned char sorted_state[] =
+{
+ 0, /* not used */
+ 3, /* sleep */
+ 1, /* ABANDONED (WAIT) */
+ 6, /* run */
+ 5, /* start */
+ 2, /* zombie */
+ 4 /* stop */
+};
+
+int
+proc_compare(pp1, pp2)
+
+struct proc **pp1;
+struct proc **pp2;
+
+{
+ register struct kinfo_proc *p1;
+ register struct kinfo_proc *p2;
+ register int result;
+ register pctcpu lresult;
+
+ /* remove one level of indirection */
+ p1 = *(struct kinfo_proc **) pp1;
+ p2 = *(struct kinfo_proc **) pp2;
+
+ /* compare percent cpu (pctcpu) */
+ if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0)
+ {
+ /* use cpticks to break the tie */
+ if ((result = PP(p2, p_cpticks) - PP(p1, p_cpticks)) == 0)
+ {
+ /* use process state to break the tie */
+ if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] -
+ sorted_state[(unsigned char) PP(p1, p_stat)]) == 0)
+ {
+ /* use priority to break the tie */
+ if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0)
+ {
+ /* use resident set size (rssize) to break the tie */
+ if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0)
+ {
+ /* use total memory to break the tie */
+ result = PROCSIZE(p2) - PROCSIZE(p1);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ result = lresult < 0 ? -1 : 1;
+ }
+
+ return(result);
+}
+
+
+/*
+ * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
+ * the process does not exist.
+ * It is EXTREMLY IMPORTANT that this function work correctly.
+ * If top runs setuid root (as in SVR4), then this function
+ * is the only thing that stands in the way of a serious
+ * security problem. It validates requests for the "kill"
+ * and "renice" commands.
+ */
+
+int proc_owner(pid)
+
+int pid;
+
+{
+ register int cnt;
+ register struct kinfo_proc **prefp;
+ register struct kinfo_proc *pp;
+
+ prefp = pref;
+ cnt = pref_len;
+ while (--cnt >= 0)
+ {
+ pp = *prefp++;
+ if (PP(pp, p_pid) == (pid_t)pid)
+ {
+ return((int)EP(pp, e_pcred.p_ruid));
+ }
+ }
+ return(-1);
+}
+
+
+/*
+ * swapmode is based on a program called swapinfo written
+ * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
+ */
+
+#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(nlst[idx].n_value, p, s, msg)
+#define KGET2(addr, p, s, msg) \
+ if (kvm_read(kd, (u_long)(addr), p, s) != s) { \
+ warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \
+ return (0); \
+ }
+#define KGETRET(addr, p, s, msg) \
+ if (kvm_read(kd, (u_long)(addr), p, s) != s) { \
+ warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \
+ return (0); \
+ }
+
+
+int
+swapmode(retavail, retfree)
+ int *retavail;
+ int *retfree;
+{
+ char *header;
+ int hlen, nswap, nswdev, dmmax;
+ int i, div, avail, nfree, npfree, used;
+ struct swdevt *sw;
+ long blocksize, *perdev;
+ u_long ptr;
+ struct rlist head;
+#if __FreeBSD_version >= 220000
+ struct rlisthdr swaplist;
+#else
+ struct rlist *swaplist;
+#endif
+ struct rlist *swapptr;
+
+ /*
+ * Counter for error messages. If we reach the limit,
+ * stop reading information from swap devices and
+ * return zero. This prevent endless 'bad address'
+ * messages.
+ */
+ static warning = 10;
+
+ if (warning <= 0) {
+ /* a single warning */
+ if (!warning) {
+ warning--;
+ fprintf(stderr,
+ "Too much errors, stop reading swap devices ...\n");
+ (void)sleep(3);
+ }
+ return(0);
+ }
+ warning--; /* decrease counter, see end of function */
+
+ KGET(VM_NSWAP, nswap);
+ if (!nswap) {
+ fprintf(stderr, "No swap space available\n");
+ return(0);
+ }
+
+ KGET(VM_NSWDEV, nswdev);
+ KGET(VM_DMMAX, dmmax);
+ KGET1(VM_SWAPLIST, &swaplist, sizeof(swaplist), "swaplist");
+ if ((sw = (struct swdevt *)malloc(nswdev * sizeof(*sw))) == NULL ||
+ (perdev = (long *)malloc(nswdev * sizeof(*perdev))) == NULL)
+ err(1, "malloc");
+ KGET1(VM_SWDEVT, &ptr, sizeof ptr, "swdevt");
+ KGET2(ptr, sw, nswdev * sizeof(*sw), "*swdevt");
+
+ /* Count up swap space. */
+ nfree = 0;
+ memset(perdev, 0, nswdev * sizeof(*perdev));
+#if __FreeBSD_version >= 220000
+ swapptr = swaplist.rlh_list;
+ while (swapptr) {
+#else
+ while (swaplist) {
+#endif
+ int top, bottom, next_block;
+#if __FreeBSD_version >= 220000
+ KGET2(swapptr, &head, sizeof(struct rlist), "swapptr");
+#else
+ KGET2(swaplist, &head, sizeof(struct rlist), "swaplist");
+#endif
+
+ top = head.rl_end;
+ bottom = head.rl_start;
+
+ nfree += top - bottom + 1;
+
+ /*
+ * Swap space is split up among the configured disks.
+ *
+ * For interleaved swap devices, the first dmmax blocks
+ * of swap space some from the first disk, the next dmmax
+ * blocks from the next, and so on up to nswap blocks.
+ *
+ * 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.
+ */
+ while (top / dmmax != bottom / dmmax) {
+ next_block = ((bottom + dmmax) / dmmax);
+ perdev[(bottom / dmmax) % nswdev] +=
+ next_block * dmmax - bottom;
+ bottom = next_block * dmmax;
+ }
+ perdev[(bottom / dmmax) % nswdev] +=
+ top - bottom + 1;
+
+#if __FreeBSD_version >= 220000
+ swapptr = head.rl_next;
+#else
+ swaplist = head.rl_next;
+#endif
+ }
+
+ header = getbsize(&hlen, &blocksize);
+ div = blocksize / 512;
+ avail = npfree = 0;
+ for (i = 0; i < nswdev; i++) {
+ int xsize, xfree;
+
+ /*
+ * Don't report statistics for partitions which have not
+ * yet been activated via swapon(8).
+ */
+
+ xsize = sw[i].sw_nblks;
+ xfree = perdev[i];
+ used = xsize - xfree;
+ npfree++;
+ avail += xsize;
+ }
+
+ /*
+ * If only one partition has been set up via swapon(8), we don't
+ * need to bother with totals.
+ */
+ *retavail = avail / 2;
+ *retfree = nfree / 2;
+ used = avail - nfree;
+ free(sw); free(perdev);
+
+ /* increase counter, no errors occurs */
+ warning++;
+
+ return (int)(((double)used / (double)avail * 100.0) + 0.5);
+}
diff --git a/usr.bin/top/sigdesc.h b/usr.bin/top/sigdesc.h
new file mode 100644
index 0000000..75bc9d7
--- /dev/null
+++ b/usr.bin/top/sigdesc.h
@@ -0,0 +1,42 @@
+/* This file was automatically generated */
+/* by the awk script "sigconv.awk". */
+
+struct sigdesc {
+ char *name;
+ int number;
+};
+
+struct sigdesc sigdesc[] = {
+ "HUP", 1,
+ "INT", 2,
+ "QUIT", 3,
+ "ILL", 4,
+ "TRAP", 5,
+ "ABRT", 6,
+ "EMT", 7,
+ "FPE", 8,
+ "KILL", 9,
+ "BUS", 10,
+ "SEGV", 11,
+ "SYS", 12,
+ "PIPE", 13,
+ "ALRM", 14,
+ "TERM", 15,
+ "URG", 16,
+ "STOP", 17,
+ "TSTP", 18,
+ "CONT", 19,
+ "CHLD", 20,
+ "TTIN", 21,
+ "TTOU", 22,
+ "IO", 23,
+ "XCPU", 24,
+ "XFSZ", 25,
+ "VTALRM", 26,
+ "PROF", 27,
+ "WINCH", 28,
+ "INFO", 29,
+ "USR1", 30,
+ "USR2", 31,
+ NULL, 0
+};
diff --git a/usr.bin/top/top.local.1 b/usr.bin/top/top.local.1
new file mode 100644
index 0000000..6f3ae17
--- /dev/null
+++ b/usr.bin/top/top.local.1
@@ -0,0 +1,47 @@
+.SH "FreeBSD 2.x NOTES"
+
+.SH DESCRIPTION OF MEMORY
+Mem: 9220K Active, 1032K Inact, 3284K Wired, 1MB Cache, 2M Buf, 1320K Free
+Swap: 91M Total, 79M Free, 13% Inuse, 80K In, 104 K Out
+
+.B K:
+Kilobyte
+.TP
+.B M:
+Megabyte
+.TP
+.B %:
+1/100
+.TP
+.B Active:
+number of pages active
+.TP
+.B Inact:
+number of pages inactive
+.TP
+.B Wired:
+number of pages wired down, including cached file data pages
+.TP
+.B Cache:
+number of pages used for VM-level disk caching
+.TP
+.B Buf:
+number of pages used for BIO-level disk caching
+.TP
+.B Free:
+number of pages free
+.TP
+.B Total:
+total available swap usage
+.TP
+.B Free:
+total free swap usage
+.TP
+.B Inuse:
+swap usage
+.TP
+.B In:
+pages paged in from swap devices (last interval)
+.TP
+.B Out:
+pages paged out to swap devices (last interval)
diff --git a/usr.bin/top/top.local.h b/usr.bin/top/top.local.h
new file mode 100644
index 0000000..202f6a1
--- /dev/null
+++ b/usr.bin/top/top.local.h
@@ -0,0 +1,68 @@
+/*
+ * Top - a top users display for Berkeley Unix
+ *
+ * Definitions for things that might vary between installations.
+ */
+
+/*
+ * The space command forces an immediate update. Sometimes, on loaded
+ * systems, this update will take a significant period of time (because all
+ * the output is buffered). So, if the short-term load average is above
+ * "LoadMax", then top will put the cursor home immediately after the space
+ * is pressed before the next update is attempted. This serves as a visual
+ * acknowledgement of the command. On Suns, "LoadMax" will get multiplied by
+ * "FSCALE" before being compared to avenrun[0]. Therefore, "LoadMax"
+ * should always be specified as a floating point number.
+ */
+#ifndef LoadMax
+#define LoadMax 5.0
+#endif
+
+/*
+ * "Table_size" defines the size of the hash tables used to map uid to
+ * username. The number of users in /etc/passwd CANNOT be greater than
+ * this number. If the error message "table overflow: too many users"
+ * is printed by top, then "Table_size" needs to be increased. Things will
+ * work best if the number is a prime number that is about twice the number
+ * of lines in /etc/passwd.
+ */
+#ifndef Table_size
+#define Table_size 20011
+#endif
+
+/*
+ * "Nominal_TOPN" is used as the default TOPN when Default_TOPN is Infinity
+ * and the output is a dumb terminal. If we didn't do this, then
+ * installations who use a default TOPN of Infinity will get every
+ * process in the system when running top on a dumb terminal (or redirected
+ * to a file). Note that Nominal_TOPN is a default: it can still be
+ * overridden on the command line, even with the value "infinity".
+ */
+#ifndef Nominal_TOPN
+#define Nominal_TOPN 18
+#endif
+
+#ifndef Default_TOPN
+#define Default_TOPN -1
+#endif
+
+#ifndef Default_DELAY
+#define Default_DELAY 2
+#endif
+
+/*
+ * If the local system's getpwnam interface uses random access to retrieve
+ * a record (i.e.: 4.3 systems, Sun "yellow pages"), then defining
+ * RANDOM_PW will take advantage of that fact. If RANDOM_PW is defined,
+ * then getpwnam is used and the result is cached. If not, then getpwent
+ * is used to read and cache the password entries sequentially until the
+ * desired one is found.
+ *
+ * We initially set RANDOM_PW to something which is controllable by the
+ * Configure script. Then if its value is 0, we undef it.
+ */
+
+#define RANDOM_PW 1
+#if RANDOM_PW == 0
+#undef RANDOM_PW
+#endif
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..38b9ebc
--- /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 CC
+value of 19.
+Otherwise, a
+.Dq CC
+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..32a7319
--- /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:")) != -1)
+ 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);
+}
+
+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..441305c
--- /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}${BINDIR}/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..403f4cc
--- /dev/null
+++ b/usr.bin/tput/tput.1
@@ -0,0 +1,136 @@
+.\" 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
+.\" $Id$
+.\"
+.Dd March 19, 1994
+.Dt TPUT 1
+.Os BSD 4.4
+.Sh NAME
+.Nm tput
+.Nd terminal capability interface
+.Sh SYNOPSIS
+.Nm
+.Op Fl T Ar term
+.Ar attribute
+.Sh DESCRIPTION
+The
+.Nm
+command 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,
+.Dq vt100
+or
+.Dq xterm .
+If not specified,
+.Nm
+retrieves the
+.Dq Ev TERM
+variable from the environment.
+.El
+.Pp
+The
+.Nm
+command outputs a string if the
+.Ar attribute
+is of type string; a number if it is of type integer.
+Otherwise,
+.Nm
+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
+.Dq cl
+sequence).
+.It init
+Initialize the terminal (the
+.Xr termcap
+.Dq is
+sequence).
+.It longname
+Print the descriptive name of the user's terminal type.
+.It reset
+Reset the terminal (the
+.Xr termcap
+.Dq rs
+sequence).
+.Sh DIAGNOSTICS
+The exit value of
+.Nm
+is based on the last attribute specified.
+If the attribute is of type string or of type integer,
+.Nm
+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
+exits 0 if the terminal has this attribute, and 1 if it does not.
+The
+.Nm
+command exits 2 if any error occurred.
+.Sh SEE ALSO
+.Xr termcap 3 ,
+.Xr termcap 5
+.Sh BUGS
+The
+.Nm
+command can't really distinguish between different types of attributes.
+.Pp
+Some termcap entries depend upon having a
+.Sq %
+in them that is just a
+.Sq %
+and nothing more. Right now we just warn about them if they don't
+have a valid type declaration. These warnings are sent to
+stderr.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/usr.bin/tput/tput.c b/usr.bin/tput/tput.c
new file mode 100644
index 0000000..2eb3e46
--- /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:")) != -1)
+ 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
+ */
+ warnx(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..7c0fd7c
--- /dev/null
+++ b/usr.bin/tr/str.c
@@ -0,0 +1,339 @@
+/*-
+ * 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.2 (Berkeley) 4/28/95";
+#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 = (u_char)*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[] = {
+#undef isalnum
+ { "alnum", isalnum, },
+#undef isalpha
+ { "alpha", isalpha, },
+#undef isblank
+ { "blank", isblank, },
+#undef iscntrl
+ { "cntrl", iscntrl, },
+#undef isdigit
+ { "digit", isdigit, },
+#undef isgraph
+ { "graph", isgraph, },
+#undef islower
+ { "lower", islower, },
+#undef isprint
+ { "print", isprint, },
+#undef ispunct
+ { "punct", ispunct, },
+#undef isspace
+ { "space", isspace, },
+#undef isupper
+ { "upper", isupper, },
+#undef isxdigit
+ { "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) : (u_char)*s->str++;
+ if (stopval < (u_char)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((u_char)*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;
+}
+
+/*
+ * 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 = (u_char)*++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..50b9d7c
--- /dev/null
+++ b/usr.bin/tr/tr.c
@@ -0,0 +1,293 @@
+/*
+ * 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.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <locale.h>
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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;
+
+ (void) setlocale(LC_CTYPE, "");
+
+ cflag = dflag = sflag = 0;
+ while ((ch = getopt(argc, argv, "cds")) != -1)
+ 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..cbdb103
--- /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 false 1 ,
+.Xr sh 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..b25398b
--- /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")) != -1) {
+ 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..3b57da5b
--- /dev/null
+++ b/usr.bin/tsort/tsort.1
@@ -0,0 +1,91 @@
+.\" 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 d
+.Op Fl l
+.Op Fl q
+.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 d
+Turn on debugging.
+.It Fl l
+Search for and display the longest cycle.
+Can take a very long time.
+.It Fl q
+Do not display informational messages about cycles. This is primarily
+intended for building libraries, where optimal ordering is not critical,
+and cycles occur often.
+.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..591ad16
--- /dev/null
+++ b/usr.bin/tsort/tsort.c
@@ -0,0 +1,437 @@
+/*
+ * 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.
+ *
+ * $Id: tsort.c,v 1.7 1997/03/11 14:48:14 peter Exp $
+ */
+
+#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.3 (Berkeley) 5/4/95";
+#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>
+#include <unistd.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 [-dlq] [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, quiet;
+
+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, "dlq")) != -1)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'l':
+ longest = 1;
+ break;
+ case 'q':
+ quiet = 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))) {
+ if (!quiet) {
+ 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 [-dlq] [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..9c47b70
--- /dev/null
+++ b/usr.bin/tty/tty.1
@@ -0,0 +1,84 @@
+.\" 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
+.\" $Id$
+.\"
+.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.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/usr.bin/tty/tty.c b/usr.bin/tty/tty.c
new file mode 100644
index 0000000..88cd60a
--- /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")) != -1)
+ 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..953dd17
--- /dev/null
+++ b/usr.bin/ul/ul.1
@@ -0,0 +1,108 @@
+.\" 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 environ 7 ) .
+.El
+.Sh SEE ALSO
+.Xr colcrt 1 ,
+.Xr man 1 ,
+.Xr nroff 1
+.Sh BUGS
+The
+.Xr nroff 1
+command 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..cf9f9ef
--- /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:")) != -1)
+ 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..c4d4b4b
--- /dev/null
+++ b/usr.bin/uname/uname.1
@@ -0,0 +1,98 @@
+.\" 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 3 ,
+.Xr uname 3 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm uname
+command appeared in
+.Bx 4.4 .
+.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..1245560
--- /dev/null
+++ b/usr.bin/uname/uname.c
@@ -0,0 +1,163 @@
+/*-
+ * 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.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.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")) != -1)
+ 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..25350d7
--- /dev/null
+++ b/usr.bin/uniq/uniq.c
@@ -0,0 +1,276 @@
+/*
+ * 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.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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")) != -1)
+ 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 && *str)
+ (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/units/Makefile b/usr.bin/units/Makefile
index 15679b5..b414356 100644
--- a/usr.bin/units/Makefile
+++ b/usr.bin/units/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.2 1996/04/06 06:00:58 thorpej Exp $
+# $Id$
PROG= units
diff --git a/usr.bin/units/README b/usr.bin/units/README
index ffb88a9..b7a173a 100644
--- a/usr.bin/units/README
+++ b/usr.bin/units/README
@@ -1,4 +1,4 @@
-# $Id: README,v 1.2 1996/04/06 06:00:59 thorpej Exp $
+# $Id$
This is a program which I wrote as a clone of the UNIX 'units'
command. I threw it together in a couple days, but it seems to work,
diff --git a/usr.bin/units/pathnames.h b/usr.bin/units/pathnames.h
index b315c5b..f41f207 100644
--- a/usr.bin/units/pathnames.h
+++ b/usr.bin/units/pathnames.h
@@ -1,4 +1,4 @@
-/* $Id: pathnames.h,v 1.3 1996/04/06 06:01:00 thorpej Exp $ */
+/* $Id$ */
/*
* Copyright (c) 1993 Christopher G. Demetriou
diff --git a/usr.bin/units/units.1 b/usr.bin/units/units.1
index 8916052..4ca4aca 100644
--- a/usr.bin/units/units.1
+++ b/usr.bin/units/units.1
@@ -1,10 +1,10 @@
-.\" $Id: units.1,v 1.6 1996/04/06 06:01:02 thorpej Exp $
+.\" $Id: units.1,v 1.4 1997/02/22 19:57:31 peter Exp $
.TH UNITS 1 "14 July 1993"
.SH NAME
units - conversion program
.SH SYNTAX
.B units
-[-f filename] [-qv] [from-unit to-unit]
+[-f filename] [-q] [to-unit from-unit]
.SH SUMMARY
.TP 4
.B -f filename
@@ -16,17 +16,14 @@ Suppresses prompting of the user for units and the display of statistics
about the number of units loaded.
.LP
.TP 4
-.B -v
-Prints the version number.
-.LP
-.TP 4
.B from-unit to-unit
Allows a single unit conversion to be done directly from the command
-line. No prompting will occur. The units program will print out
-only the result of this single conversion.
+line. The program will not print prompts. It will print out the
+result of the single specified conversion.
+
.SH DESCRIPTION
-The units program converts quantities expression in various scales to
+The units program converts quantities expressed in various scales to
their equivalents in other scales. The units program can only
handle multiplicative scale changes. It cannot convert Centigrade
to Fahrenheit, for example. It works interactively by prompting
@@ -43,14 +40,27 @@ the user for input:
* 0.00026417205
/ 3785.4118
+ You have: meters/s
+ You want: furlongs/fortnight
+ * 6012.8848
+ / 0.00016630952
+
+ You have: 1|2 inch
+ You want: cm
+ * 1.27
+ / 0.78740157
+
.fi
Powers of units can be specified using the '^' character as shown in
the example, or by simple concatenation: 'cm3' is equivalent to 'cm^3'.
Multiplication of units can be specified by using spaces, a dash or
an asterisk. Division of units is indicated by the slash ('/').
Note that multiplication has a higher precedence than division,
-so 'm/s/s' is the same as 'm/s^2' or 'm/s s'.
-If the user enters incompatible unit types, the units program will
+so 'm/s/s' is the same as 'm/s^2' or 'm/s s'. Division of numbers
+must be indicated using the vertical bar ('|'). To convert half a
+meter, you would write '1|2 meter'. If you write '1/2 meter' then the
+units program would interpret that as equivalent to '0.5/meter'.
+If you enter incompatible unit types, the units program will
print a message indicating that the units are not conformable and
it will display the reduced form for each unit:
.nf
@@ -60,7 +70,6 @@ it will display the reduced form for each unit:
conformability error
2.7777778e-11 kg m^2 / sec^3
2.1166667e-05 kg^2 m / sec
-
.fi
.LP
The conversion information is read from a units data file. The default
@@ -83,24 +92,49 @@ au astronomical unit
.fi
.in -4m
-\'Pound' is a unit of mass. Compound names are run together
-so 'poundforce' is a unit of force. British units that differ from their
-US counterparts are prefixed with 'br', and currency is prefixed with
-its country name: 'belgiumfranc', 'britainpound'. When searching for
-a unit, if the specified string does not appear exactly as a unit
-name, then the units program will try to remove a trailing 's' or
-a trailing 'es' and check again for a match.
+The unit 'pound' is a unit of mass. Compound names are run together
+so 'poundforce' is a unit of force. The unit 'ounce' is also a unit
+of mass. The fluid ounce is 'floz'. British units that differ from
+their US counterparts are prefixed with 'br', and currency is prefixed
+with its country name: 'belgiumfranc', 'britainpound'. When searching
+for a unit, if the specified string does not appear exactly as a unit
+name, then the units program will try to remove a trailing 's' or a
+trailing 'es' and check again for a match.
.LP
-All of these definitions can be read in the standard units file, or you
-can supply your own file. A unit is specified on a single line by
-giving its name and an equivalence. One should be careful to define
+To find out what units are available read the standard units file.
+If you want to add your own units you can supply your own file.
+A unit is specified on a single line by
+giving its name and an equivalence. Be careful to define
new units in terms of old ones so that a reduction leads to the
primitive units which are marked with '!' characters.
The units program will not detect infinite loops that could be caused
-by careless unit definitions.
+by careless unit definitions. Comments in the unit definition file
+begin with a '/' character at the beginning of a line.
.LP
Prefixes are defined in the same was as standard units, but with
-a trailing dash at the end of the prefix name.
+a trailing dash at the end of the prefix name. If a unit is not found
+even after removing trailing 's' or 'es', then it will be checked
+against the list of prefixes. Prefixes will be removed until a legal
+base unit is identified.
+.LP
+Here is an example of a short units file that defines some basic
+units.
+.in 17m
+.ta
+.ta 9m +
+.nf
+
+m !a!
+sec !b!
+micro- 1e-6
+minute 60 sec
+hour 60 min
+inch 0.0254 m
+ft 12 inches
+mile 5280 ft
+.fi
+.in -17m
+
.SH BUGS
.LP
The effect of including a '/' in a prefix is surprising.
@@ -121,4 +155,4 @@ for duplication.
.SH FILES
/usr/share/misc/units.lib - the standard units library
.SH AUTHOR
-Adrian Mariano (adrian@cam.cornell.edu or mariano@geom.umn.edu)
+Adrian Mariano (adrian@cam.cornell.edu)
diff --git a/usr.bin/units/units.c b/usr.bin/units/units.c
index 83e5b42..8ecc077 100644
--- a/usr.bin/units/units.c
+++ b/usr.bin/units/units.c
@@ -1,4 +1,4 @@
-/* $Id: units.c,v 1.6 1996/04/06 06:01:03 thorpej Exp $ */
+/* $Id$ */
/*
* units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
@@ -683,8 +683,8 @@ main(int argc, char **argv)
if (!quiet)
printf("You have: ");
if (!fgets(havestr, 80, stdin)) {
- if (!quiet);
- putchar('\n');
+ if (!quiet)
+ putchar('\n');
exit(0);
}
} while (addunit(&have, havestr, 0) ||
@@ -703,4 +703,6 @@ main(int argc, char **argv)
showanswer(&have, &want);
}
}
+
+ return(0);
}
diff --git a/usr.bin/units/units.lib b/usr.bin/units/units.lib
index ef4e8d7..9fcacc4 100644
--- a/usr.bin/units/units.lib
+++ b/usr.bin/units/units.lib
@@ -1,4 +1,4 @@
-/ $Id: units.lib,v 1.3 1996/04/06 06:01:04 thorpej Exp $
+/ $Id$
/ primitive units
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..4f6a832
--- /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 appeared in
+.Bx 4.4 .
diff --git a/usr.bin/unvis/unvis.c b/usr.bin/unvis/unvis.c
new file mode 100644
index 0000000..5ea689b
--- /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, "")) != -1)
+ 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..48a584a
--- /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, "")) != -1)
+ 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/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/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/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..1667e01
--- /dev/null
+++ b/usr.bin/uudecode/uudecode.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
+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 <fnmatch.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+char *filename;
+int cflag, pflag;
+
+void usage __P((void));
+int decode __P((void));
+int decode2 __P((int));
+
+extern int optind;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int errno;
+ int rval, ch;
+
+ while ((ch = getopt(argc, argv, "cp")) != -1) {
+ switch(ch) {
+ case 'c':
+ cflag = 1; /* multiple uudecode'd files */
+ break;
+ case 'p':
+ pflag = 1; /* print output to stdout */
+ break;
+ default:
+ (void)usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+
+ 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);
+}
+
+int
+decode ()
+{
+ int flag;
+
+ /* decode only one file per input stream */
+ if (!cflag)
+ return(decode2(0));
+
+ /* multiple uudecode'd files */
+ for (flag = 0; ; flag++)
+ if (decode2(flag))
+ return(1);
+ else if (feof(stdin))
+ break;
+
+ return(0);
+}
+
+int
+decode2(flag)
+ int flag;
+{
+ extern int errno;
+ struct passwd *pw;
+ register int n;
+ register char ch, *p;
+ int mode, n1;
+ char buf[MAXPATHLEN];
+ char buffn[MAXPATHLEN]; /* file name buffer */
+
+
+ /* search for header line */
+ do {
+ if (!fgets(buf, sizeof(buf), stdin)) {
+ if (flag) /* no error */
+ return(0);
+
+ (void)fprintf(stderr,
+ "uudecode: %s: no \"begin\" line\n", filename);
+ return(1);
+ }
+ } while (strncmp(buf, "begin ", 6) ||
+ fnmatch("begin [0-7]* *", buf, 0));
+
+ (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 (pflag)
+ ; /* print to stdout */
+
+ else if (!freopen(buf, "w", stdout) ||
+ fchmod(fileno(stdout), mode&0666)) {
+ (void)fprintf(stderr, "uudecode: %s: %s: %s\n", buf,
+ filename, strerror(errno));
+ return(1);
+ }
+ strcpy(buffn, buf); /* store file name from header line */
+
+ /* 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 */
+#define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) )
+/* #define IS_DEC(c) (1) */
+
+#define OUT_OF_RANGE \
+{ \
+ (void)fprintf(stderr, \
+ "uudecode:\n\tinput file: %s\n\tencoded file: %s\n\t%s: [%d-%d]\n", \
+ filename, buffn, "character out of range", 1 + ' ', 077 + ' ' + 1); \
+ return(1); \
+}
+
+
+ /*
+ * `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) {
+ if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) &&
+ IS_DEC(*(p + 2)) && IS_DEC(*(p + 3))))
+ OUT_OF_RANGE
+
+ 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) {
+ if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
+ OUT_OF_RANGE
+ ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
+ putchar(ch);
+ }
+ if (n >= 2) {
+ if (!(IS_DEC(*(p + 1)) &&
+ IS_DEC(*(p + 2))))
+ OUT_OF_RANGE
+
+ ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
+ putchar(ch);
+ }
+ if (n >= 3) {
+ if (!(IS_DEC(*(p + 2)) &&
+ IS_DEC(*(p + 3))))
+ OUT_OF_RANGE
+ ch = DEC(p[2]) << 6 | DEC(p[3]);
+ putchar(ch);
+ }
+ }
+ }
+ if (fgets(buf, sizeof(buf), stdin) == NULL ||
+ (strcmp(buf, "end") && strcmp(buf, "end\n") &&
+ strcmp(buf, "end\r\n"))) {
+ (void)fprintf(stderr, "uudecode: %s: no \"end\" line.\n",
+ filename);
+ return(1);
+ }
+ return(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: uudecode [-cp] [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..3dd1e68
--- /dev/null
+++ b/usr.bin/uuencode/uuencode.1
@@ -0,0 +1,132 @@
+.\" 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
+.\" $Id$
+.\"
+.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 Fl cp
+.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.
+.Pp
+The following options are available for
+.Nm uudecode :
+.Bl -tag -width ident
+.It Fl c
+Decode more than one uuencode'd file from
+.Ar file
+if possible.
+.It Fl p
+Decode
+.Ar file
+and write output to standard output.
+.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
+
+The following example unpack all uuencode'd
+files from your mailbox into your current working directory.
+.Pp
+.Bd -literal -offset indent -compact
+uudecode -c < $MAIL
+.Ed
+
+The following example extract a compress'ed tar
+archive from your mailbox
+.Pp
+.Bd -literal -offset indent -compact
+uudecode -p < $MAIL | zcat | tar xfv -
+.Ed
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr mail 1 ,
+.Xr uucp 1 ,
+.Xr uuencode 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..8580624
--- /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, "") != -1)
+ 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..c2a89e1
--- /dev/null
+++ b/usr.bin/uuencode/uuencode.format.5
@@ -0,0 +1,104 @@
+.\" 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 1 to 45 or 64 and can
+be determined by subtracting the character space (octal 40)
+from the character.
+Character 64 represents a count of zero.
+.Pp
+Groups of 3 bytes are stored in 4 characters, 6 bits per character.
+All characters are always in range from 1 to 64 and are offset by a
+space (octal 40) to make the characters printing. Character
+64 represents a count of zero.
+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
+backquote (octal 140) character.
+.Pp
+The trailer line consists of
+.Dq end
+on a line by itself.
+.Sh SEE ALSO
+.Xr mail 1 ,
+.Xr uucp 1 ,
+.Xr uudecode 1 ,
+.Xr uuencode 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..6fc4795
--- /dev/null
+++ b/usr.bin/vacation/Makefile
@@ -0,0 +1,7 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id$
+
+PROG= vacation
+CFLAGS+= -Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/vacation/vacation.1 b/usr.bin/vacation/vacation.1
new file mode 100644
index 0000000..8ce05e0
--- /dev/null
+++ b/usr.bin/vacation/vacation.1
@@ -0,0 +1,179 @@
+.\" 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.
+.\"
+.\" From: @(#)vacation.1 8.1 (Berkeley) 6/16/93
+.\" $Id$
+.\"
+.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
+.Fl l
+.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.
+.It Fl l
+Print the contents of the vacation database files. For each entry,
+the address the reply has been sent to and the associated time will
+be printed to standard output.
+.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 a
+.Xr hash 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 3 .
+.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 syslog 3 ,
+.Xr sendmail 8 ,
+.Xr syslogd 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..6cfdfc8
--- /dev/null
+++ b/usr.bin/vacation/vacation.c
@@ -0,0 +1,486 @@
+/*
+ * 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[] = "From: @(#)vacation.c 8.2 (Berkeley) 1/26/94";
+static char rcsid[] =
+ "$Id: vacation.c,v 1.8 1997/04/23 22:36:51 ache Exp $";
+#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];
+
+static int isdelim __P((int));
+static int junkmail __P((void));
+static void listdb __P((void));
+static int nsearch __P((char *, char *));
+static void readheaders __P((void));
+static int recent __P((void));
+static void sendmessage __P((char *));
+static void setinterval __P((time_t));
+static void setreply __P((void));
+static void usage __P((void));
+
+int
+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, lflag;
+
+ opterr = iflag = lflag = 0;
+ interval = -1;
+ while ((ch = getopt(argc, argv, "a:Iilr:")) != -1)
+ 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 'l':
+ lflag = 1; /* list the database */
+ 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 && !lflag)
+ 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 (lflag)
+ listdb();
+ else if (interval != -1)
+ setinterval(interval);
+
+ if (iflag || lflag) {
+ (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
+ */
+static void
+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.
+ */
+static int
+nsearch(name, str)
+ register char *name, *str;
+{
+ register int len;
+
+ for (len = strlen(name); *str; ++str)
+ if (*str == *name &&
+ !strncasecmp(name, str, len) &&
+ isdelim((unsigned char)str[len]))
+ return(1);
+ return(0);
+}
+
+/*
+ * junkmail --
+ * read the header and return if automagic/junk/bulk/list mail
+ */
+static int
+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
+ */
+static int
+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
+ */
+static void
+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.
+ */
+static void
+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
+ */
+static void
+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]);
+ close(fileno(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);
+}
+
+static void
+usage()
+{
+ syslog(LOG_NOTICE, "uid %u: usage: vacation [-i [-rinterval]] [-l] [-a alias] login\n",
+ getuid());
+ exit(1);
+}
+
+static void
+listdb()
+{
+ DBT key, data;
+ int rv;
+ time_t t;
+ char user[MAXLINE];
+
+ while((rv = db->seq(db, &key, &data, R_NEXT)) == 0) {
+ bcopy(key.data, user, key.size);
+ user[key.size] = '\0';
+ if (strcmp(user, VIT) == 0)
+ continue;
+ bcopy(data.data, &t, data.size);
+ printf("%-40s %-10s", user, ctime(&t));
+ }
+ if (rv == -1)
+ perror("IO error in database");
+}
+
+/*
+ * Is `c' a delimiting character for a recipient name?
+ */
+static int
+isdelim(c)
+ int c;
+{
+ /*
+ * NB: don't use setlocale() before, headers are supposed to
+ * consist only of ASCII (aka. C locale) characters.
+ */
+ if (isalnum(c))
+ return(0);
+ if (c == '_' || c == '-' || c == '.')
+ return(0);
+ return(1);
+}
diff --git a/usr.bin/vgrind/Makefile b/usr.bin/vgrind/Makefile
new file mode 100644
index 0000000..5682b25
--- /dev/null
+++ b/usr.bin/vgrind/Makefile
@@ -0,0 +1,27 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+
+PROG= vfontedpr
+SRCS= regexp.c vfontedpr.c
+MAN1= vgrind.1
+MAN5= vgrindefs.5
+BINDIR= /usr/libexec
+BIN2DIR=/usr/bin
+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}${BIN2DIR}/vgrind
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/vgrindefs.src \
+ ${DESTDIR}/usr/share/misc/vgrindefs
+ ${INSTALL} ${COPY} -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..d606620
--- /dev/null
+++ b/usr.bin/vgrind/regexp.c
@@ -0,0 +1,598 @@
+/*
+ * 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;
+
+ /* return 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 == '_' ||
+ /* C++ destructor */
+ *s1 == '~' ||
+ /* C++ scope operator */
+ (strlen(s1) > 1 && *s1 == ':' && s1[1] == ':' &&
+ (s1++, TRUE))))
+ 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..b471044
--- /dev/null
+++ b/usr.bin/vgrind/vfontedpr.c
@@ -0,0 +1,724 @@
+/*
+ * 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_nocom; /* regexp for non-comments */
+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);
+ if (cgetustr(defs, "nc", &cp) >= 0)
+ l_nocom = 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 */
+ char *nocomptr; /* end of a non-comment delimiter */
+
+ _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] = '\0';
+ 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);
+ nocomptr = expmatch (s, l_nocom, dummy);
+
+ /* start of non-comment? */
+ if (nocomptr != NIL)
+ if ((nocomptr <= comptr || comptr == NIL)
+ && (nocomptr <= acmptr || acmptr == NIL)) {
+ /* continue after non-comment */
+ putKcp (s, nocomptr-1, FALSE);
+ s = nocomptr;
+ continue;
+ }
+
+ /* 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;
+ if (blklevel > 0 /* sanity */)
+ 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((unsigned char)*start++);
+ while (--i > 0);
+ ps("\\*(-K");
+ continue;
+ }
+ }
+
+ putcp ((unsigned char)*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 '\r':
+ 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..a866103
--- /dev/null
+++ b/usr.bin/vgrind/vgrind.1
@@ -0,0 +1,236 @@
+.\" 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 Ns Ar language
+.Op Fl n
+.Op Fl p Ar postproc
+.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 the postprocessor
+for output,
+.Xr psroff 1
+by default.
+.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 C++
+.Pq Fl l Ns Ar c++ ,
+.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 ,
+.Tn ICON
+.Pq Fl l Ns Ar I ,
+and
+.Tn PERL
+.Pq Fl l Ns Ar perl .
+.It Fl n
+forces no keyword bolding
+.It Fl p Ar postproc
+use
+.Ar postproc
+to post-process the output,
+.Xr psroff 1
+by default.
+.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..ad459c5
--- /dev/null
+++ b/usr.bin/vgrind/vgrind.sh
@@ -0,0 +1,155 @@
+#!/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
+set postproc=psroff
+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 -p:
+ if ($#argv < 2) then
+ echo "vgrind: $1:q option must have argument"
+ goto done
+ else
+ set postproc="$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 "$postproc -rx1 $voptions -i -mvgrind 2>> xindex"
+ else
+ $vf $options $files | \
+ sh -c "$postproc -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 | $postproc -i $voptions -mvgrind
+ else
+ $vf $options $files | $postproc -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..9b65c2c
--- /dev/null
+++ b/usr.bin/vgrind/vgrindefs.5
@@ -0,0 +1,169 @@
+.\" 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 "ab str regular expression for the start of an alternate comment"
+.It "ae str regular expression for the end of an alternate comment"
+.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 "nc str regular expression for a non-comment (see below)"
+.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
+Non-comments are required to describe a certain context where a
+sequence that would normally start a comment loses its special
+meaning. A typical example for this can be found in Perl, where
+comments are normally starting with
+.Ql # ,
+while the string
+.Ql $#
+is an operator on an array.
+.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 troff 1 ,
+.Xr vgrind 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..c19e836
--- /dev/null
+++ b/usr.bin/vgrind/vgrindefs.src
@@ -0,0 +1,159 @@
+# 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=^\a?\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=^\a?\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:
+#
+# Hack alert: defining function calls as `alternate comments' (ab/ae) seems
+# to be the only way to avoid major confusion inside vfontedpr for calls like:
+# &packagename'function;
+#
+Perl|perl|pl:\
+ :pb=sub\d\p\d:bb={:be=}:cb=#:ce=$:nc=\$#:tl:\
+ :ab=&:ae=(;|\d|,):\
+ :sb=":se=(\e"|$):lb=':le=(\e'|$):\
+ :kw=do if unless while until else elsif for foreach continue\
+ next redo sub last goto return die exit require:
diff --git a/usr.bin/vi/Makefile b/usr.bin/vi/Makefile
new file mode 100644
index 0000000..d05179d
--- /dev/null
+++ b/usr.bin/vi/Makefile
@@ -0,0 +1,202 @@
+#
+# $Id: Makefile,v 1.16 1997/04/12 14:34:02 peter Exp $
+#
+# This has most of the glue needed to compile tknvi and the perl hooks,
+# but not all.
+#
+
+SRCDIR= ${.CURDIR}/../../contrib/nvi
+
+.if defined(RELEASE_BUILD_FIXIT)
+# When building `vi' for the fixit floppy, don't include any of the
+# API stuff.
+APISTUFF= ex_notcl.c ex_noperl.c
+
+.else
+
+.if !defined(NOTCL)
+TCLINTERP= yes #we have it in the base tree, little cost to vi
+.endif
+#TKNVI= yes #not ready, needs X11, tk, doesn't quite work yet
+#PERLINTERP= yes #needs the perl5 v5.003 port
+
+APISTUFF= ex_tcl.c ex_perl.c
+
+# Any better ideas?
+#PERL= /usr/local/bin/perl5.003
+#CFLAGS+= -DHAVE_PERL_5_003_01 # If perl >= 5.03.01
+
+.endif
+
+CFLAGS+= -DGTAGS
+
+#if using ncurses:
+#CFLAGS+= -DSYSV_CURSES
+
+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= ${SRCDIR}/docs/USD.doc/vi.man/vi.1
+MLINKS+=vi.1 ex.1 vi.1 view.1
+MLINKS+=vi.1 nex.1 vi.1 nview.1 vi.1 nvi.1
+
+CATALOGS= dutch english french german ru_SU.KOI8-R spanish swedish
+NLLINKS= nl_NL
+ENLINKS= en_AU en_CA en_GB en_US
+FRLINKS= fr_BE fr_CA fr_CH fr_FR
+DELINKS= de_AT de_CH de_DE
+ESLINKS= es_ES
+SVLINKS= sv_SE
+
+.PATH: ${SRCDIR}/common
+.PATH: ${SRCDIR}/ex
+.PATH: ${SRCDIR}/cl
+.PATH: ${SRCDIR}/vi
+
+CFLAGS+=-I${.CURDIR} -I${SRCDIR} -I${SRCDIR}/include
+
+.if !defined(TKNVI)
+DPADD+= ${LIBCURSES} ${LIBTERMCAP}
+LDADD+= -lcurses -ltermcap
+.endif
+
+.if defined(TKNVI)
+.PATH: ${SRCDIR}/tk
+LDADD+= -L/usr/local/lib -L/usr/X11R6/lib -ltk41 -lX11
+CFLAGS+= -I/usr/local/include -I/usr/X11R6/include
+.endif
+
+.if defined(TCLINTERP)
+.PATH: ${SRCDIR}/tcl_api
+DPADD+= ${LIBTCL} ${LIBM}
+LDADD+= -ltcl -lm
+CFLAGS+= -DHAVE_TCL_INTERP
+.endif
+
+.if defined(PERLINTERP)
+.PATH: ${SRCDIR}/perl_api
+
+# Perl "knows" how to compile it's components. Ask it for details...
+PERLLIB!= ${PERL} -MConfig -e 'print $$Config{privlib}'
+PERLCPPFLAGS!= cd ${SRCDIR}/build; ${PERL} -MExtUtils::Embed -e 'ccflags;perl_inc'
+PERLLIBS!= cd ${SRCDIR}/build; ${PERL} -MExtUtils::Embed -e 'ldopts'
+PERLLDFLAGS!= cd ${SRCDIR}/build; ${PERL} -MExtUtils::Embed -e 'ccdlflags'
+
+LDADD+= ${PERLLDFLAGS} ${PERLLIBS}
+CFLAGS+= -DHAVE_PERL_INTERP ${PERLCPPFLAGS}
+
+.endif
+
+CLEANFILES+=${EX}
+
+# Vi curses sources
+.if !defined(TKNVI)
+SRCS+= cl_bsd.c cl_funcs.c cl_main.c cl_read.c cl_screen.c cl_term.c
+.endif
+
+# Vi Tk sources
+.if defined(TKNVI)
+SRCS+= tk_funcs.c tk_main.c tk_read.c tk_screen.c tk_term.c tk_util.c
+.endif
+
+# Vi Tcl/Perl interpreter sources
+.if defined(TCLINTERP) || defined(PERLINTERP)
+SRCS+= api.c
+.endif
+.if defined(TCLINTERP)
+SRCS+= tcl.c
+.endif
+.if defined(PERLINTERP)
+# perl.c is generated
+SRCS+= perl.c perlsfio.c
+.endif
+
+# General sources.
+SRCS+= cut.c delete.c exf.c key.c line.c log.c main.c mark.c msg.c options.c \
+ options_f.c put.c screen.c search.c seq.c recover.c util.c
+
+# 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_cmd.c ex_cscope.c ex_delete.c ex_display.c \
+ ex_edit.c ex_equal.c ex_file.c ex_filter.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_quit.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_txt.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 ${APISTUFF}
+
+# Vi source.
+SRCS+= getc.c v_at.c v_ch.c v_cmd.c v_delete.c v_ex.c v_increment.c v_init.c \
+ v_itxt.c v_left.c v_mark.c v_match.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_txt.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 vi.c
+
+# Vi screen source.
+SRCS+= vs_line.c vs_msg.c vs_refresh.c vs_relative.c vs_smap.c vs_split.c
+
+# Generate perl.c
+.if defined(PERLINTERP)
+perl.c: perl.xs typemap
+ echo "#define _PATH_PERLSCRIPTS \"/usr/share/vi/perl\"" > $@
+ $(PERL) $(PERLLIB)/ExtUtils/xsubpp -typemap \
+ $(PERLLIB)/ExtUtils/typemap $(SRCDIR)/perl_api/perl.xs >> $@
+ ($(PERL) -ne 'print "sub $$1 {\$$curscr->$$1(\@_)}\n" \
+ if /newXS\("VI::([^":]*)"/;' $@ ; echo "1;") > VI.pm
+
+CLEANFILES+= VI.pm perl.c
+.endif
+
+# unifdef has some *weird* exit codes, sigh! RTFM unifdef(1)...
+ex_notcl.c: ex_tcl.c
+ -unifdef -UHAVE_TCL_INTERP ${SRCDIR}/ex/ex_tcl.c > ${.TARGET}
+
+ex_noperl.c: ex_perl.c
+ -unifdef -UHAVE_PERL_INTERP ${SRCDIR}/ex/ex_perl.c > ${.TARGET}
+
+CLEANFILES+= ex_notcl.c ex_noperl.c
+
+afterinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 \
+ ${CATALOGS:S;^;${SRCDIR}/catalog/;} \
+ ${DESTDIR}/usr/share/vi/catalog
+ for l in ${NLLINKS}; do \
+ ln -fs dutch ${DESTDIR}/usr/share/vi/catalog/$$l.ISO_8859-1; \
+ done
+ for l in ${ENLINKS}; do \
+ ln -fs english ${DESTDIR}/usr/share/vi/catalog/$$l.ISO_8859-1; \
+ done
+ ln -fs english ${DESTDIR}/usr/share/vi/catalog/us-ascii
+ for l in ${FRLINKS}; do \
+ ln -fs french ${DESTDIR}/usr/share/vi/catalog/$$l.ISO_8859-1; \
+ done
+ for l in ${DELINKS}; do \
+ ln -fs german ${DESTDIR}/usr/share/vi/catalog/$$l.ISO_8859-1; \
+ done
+ for l in ${ESLINKS}; do \
+ ln -fs spanish ${DESTDIR}/usr/share/vi/catalog/$$l.ISO_8859-1; \
+ done
+ for l in ${SVLINKS}; do \
+ ln -fs swedish ${DESTDIR}/usr/share/vi/catalog/$$l.ISO_8859-1; \
+ done
+ ln -fs ru_SU.KOI8-R ${DESTDIR}/usr/share/vi/catalog/ru_RU.KOI8-R
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 \
+ ${SRCDIR}/tcl_scripts/*.tcl \
+ ${DESTDIR}/usr/share/vi/tcl
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 \
+ ${SRCDIR}/perl_scripts/*.pl \
+ ${DESTDIR}/usr/share/vi/perl
+.if defined(PERLINTERP)
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m 444 VI.pm \
+ ${DESTDIR}/usr/share/vi/perl
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/vi/config.h b/usr.bin/vi/config.h
new file mode 100644
index 0000000..5a867f1
--- /dev/null
+++ b/usr.bin/vi/config.h
@@ -0,0 +1,194 @@
+/* config.h. Generated automatically by configure. */
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define to empty if the keyword does not work. */
+/* #undef const */
+
+/* Define if you have a working `mmap' system call. */
+#define HAVE_MMAP 1
+
+/* Define if your struct stat has st_blksize. */
+#define HAVE_ST_BLKSIZE 1
+
+/* Define if you have <vfork.h>. */
+/* #undef HAVE_VFORK_H */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef mode_t */
+
+/* Define to `long' if <sys/types.h> doesn't define. */
+/* #undef off_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef pid_t */
+
+/* Define to `unsigned' if <sys/types.h> doesn't define. */
+/* #undef size_t */
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if your <sys/time.h> declares struct tm. */
+/* #undef TM_IN_SYS_TIME */
+
+/* Define vfork as fork if vfork does not work. */
+/* #undef vfork */
+
+/* Define if your processor stores words with the most significant
+ byte first (like Motorola and SPARC, unlike Intel and VAX). */
+/* #undef WORDS_BIGENDIAN */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef ssize_t */
+
+/* Define if you want a debugging version. */
+/* #undef DEBUG */
+
+/* Define if you have a System V-style (broken) gettimeofday. */
+/* #undef HAVE_BROKEN_GETTIMEOFDAY */
+
+/* Define if you have a Ultrix-style (broken) vdisable. */
+/* #undef HAVE_BROKEN_VDISABLE */
+
+/* Define if you have a BSD version of curses. */
+#ifndef SYSV_CURSES
+#define HAVE_BSD_CURSES 1
+#endif
+
+/* Define if you have the curses(3) addnstr function. */
+#define HAVE_CURSES_ADDNSTR 1
+
+/* Define if you have the curses(3) beep function. */
+#ifdef SYSV_CURSES
+#define HAVE_CURSES_BEEP 1
+#endif
+
+/* Define if you have the curses(3) flash function. */
+#ifdef SYSV_CURSES
+#define HAVE_CURSES_FLASH 1
+#endif
+
+/* Define if you have the curses(3) idlok function. */
+#define HAVE_CURSES_IDLOK 1
+
+/* Define if you have the curses(3) keypad function. */
+#ifdef SYSV_CURSES
+#define HAVE_CURSES_KEYPAD 1
+#endif
+
+/* Define if you have the curses(3) newterm function. */
+#ifdef SYSV_CURSES
+#define HAVE_CURSES_NEWTERM 1
+#endif
+
+/* Define if you have the curses(3) setupterm function. */
+#ifdef SYSV_CURSES
+#define HAVE_CURSES_SETUPTERM 1
+#endif
+
+/* Define if you have the curses(3) tigetstr/tigetnum functions. */
+#ifdef SYSV_CURSES
+#define HAVE_CURSES_TIGETSTR 1
+#endif
+
+/* Define if you have the chsize(2) system call. */
+/* #undef HAVE_FTRUNCATE_CHSIZE */
+
+/* Define if you have the ftruncate(2) system call. */
+#define HAVE_FTRUNCATE_FTRUNCATE 1
+
+/* Define if you have fcntl(2) style locking. */
+/* #undef HAVE_LOCK_FCNTL */
+
+/* Define if you have flock(2) style locking. */
+#define HAVE_LOCK_FLOCK 1
+
+/* Define if you want to compile in the Perl interpreter. */
+/* #undef HAVE_PERL_INTERP */ /* XXX: SET IN Makefile CFLAGS */
+
+/* Define if your Perl is at least 5.003_01. */
+/* #undef HAVE_PERL_5_003_01 */ /* XXX: SET IN Makefile CFLAGS */
+
+/* Define if you have the Berkeley style revoke(2) system call. */
+#define HAVE_REVOKE 1
+
+/* Define if you have <sys/mman.h> */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define if you have <sys/select.h> */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define if you have the System V style pty calls. */
+/* #undef HAVE_SYS5_PTY */
+
+/* Define if you want to compile in the Tcl interpreter. */
+/* #define HAVE_TCL_INTERP */ /* XXX: SET IN Makefile CFLAGS */
+
+/* Define if your sprintf returns a pointer, not a length. */
+/* #undef SPRINTF_RET_CHARPNT */
+
+/* Define if you have the bsearch function. */
+#define HAVE_BSEARCH 1
+
+/* Define if you have the gethostname function. */
+#define HAVE_GETHOSTNAME 1
+
+/* Define if you have the getopt function. */
+#define HAVE_GETOPT 1
+
+/* Define if you have the getpagesize function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define if you have the memchr function. */
+#define HAVE_MEMCHR 1
+
+/* Define if you have the memcpy function. */
+#define HAVE_MEMCPY 1
+
+/* Define if you have the memmove function. */
+#define HAVE_MEMMOVE 1
+
+/* Define if you have the memset function. */
+#define HAVE_MEMSET 1
+
+/* Define if you have the mkstemp function. */
+#define HAVE_MKSTEMP 1
+
+/* Define if you have the mmap function. */
+#define HAVE_MMAP 1
+
+/* Define if you have the select function. */
+#define HAVE_SELECT 1
+
+/* Define if you have the setenv function. */
+#define HAVE_SETENV 1
+
+/* Define if you have the snprintf function. */
+#define HAVE_SNPRINTF 1
+
+/* Define if you have the strdup function. */
+#define HAVE_STRDUP 1
+
+/* Define if you have the strerror function. */
+#define HAVE_STRERROR 1
+
+/* Define if you have the strpbrk function. */
+#define HAVE_STRPBRK 1
+
+/* Define if you have the strsep function. */
+#define HAVE_STRSEP 1
+
+/* Define if you have the strtol function. */
+#define HAVE_STRTOL 1
+
+/* Define if you have the strtoul function. */
+#define HAVE_STRTOUL 1
+
+/* Define if you have the unsetenv function. */
+#define HAVE_UNSETENV 1
+
+/* Define if you have the valloc function. */
+#define HAVE_VALLOC 1
+
+/* Define if you have the vsnprintf function. */
+#define HAVE_VSNPRINTF 1
diff --git a/usr.bin/vi/pathnames.h b/usr.bin/vi/pathnames.h
new file mode 100644
index 0000000..f469351
--- /dev/null
+++ b/usr.bin/vi/pathnames.h
@@ -0,0 +1,45 @@
+/* @(#)pathnames.h.in 8.4 (Berkeley) 6/26/96 */
+
+#ifndef _PATH_BSHELL
+#define _PATH_BSHELL "/bin/sh"
+#endif
+
+#ifndef _PATH_EXRC
+#define _PATH_EXRC ".exrc"
+#endif
+
+#ifndef _PATH_MSGCAT
+#define _PATH_MSGCAT "/usr/share/vi/catalog/"
+#endif
+
+#ifndef _PATH_NEXRC
+#define _PATH_NEXRC ".nexrc"
+#endif
+
+#ifndef _PATH_PRESERVE
+#define _PATH_PRESERVE "/var/tmp/vi.recover"
+#endif
+
+#ifndef _PATH_SYSV_PTY
+#define _PATH_SYSV_PTY "/dev/ptmx"
+#endif
+
+#ifndef _PATH_SENDMAIL
+#define _PATH_SENDMAIL "/usr/sbin/sendmail"
+#endif
+
+#ifndef _PATH_SYSEXRC
+#define _PATH_SYSEXRC "/etc/vi.exrc"
+#endif
+
+#ifndef _PATH_TAGS
+#define _PATH_TAGS "tags"
+#endif
+
+#ifndef _PATH_TMP
+#define _PATH_TMP "/tmp"
+#endif
+
+#ifndef _PATH_TTY
+#define _PATH_TTY "/dev/tty"
+#endif
diff --git a/usr.bin/vi/port.h b/usr.bin/vi/port.h
new file mode 100644
index 0000000..21f16c9
--- /dev/null
+++ b/usr.bin/vi/port.h
@@ -0,0 +1,185 @@
+/* @(#)port.h.in 8.13 (Berkeley) 6/12/96 */
+
+/*
+ * Declare the basic types, if they aren't already declared. Named and
+ * some system's db.h files protect them with __BIT_TYPES_DEFINED__.
+ */
+#ifndef __BIT_TYPES_DEFINED__
+#define __BIT_TYPES_DEFINED__
+
+
+
+
+
+#endif
+
+
+
+
+
+
+/*
+ * XXX
+ * Handle function prototypes. This steps on name space that vi doesn't
+ * control, but all of the other solutions are worse.
+ */
+#undef __P
+#if defined(__STDC__) || defined(__cplusplus)
+#define __P(protos) protos /* ANSI C prototypes */
+#else
+#define __P(protos) () /* K&R C preprocessor */
+#endif
+
+/*
+ * XXX
+ * Some versions of System V changed the number of arguments to gettimeofday
+ * without changing the name.
+ */
+#ifdef HAVE_BROKEN_GETTIMEOFDAY
+#define gettimeofday(tv, tz) gettimeofday(tv)
+#endif
+
+/*
+ * XXX
+ * If we don't have mmap, we fake it with read and write, but we'll
+ * still need the header information.
+ */
+#ifndef HAVE_SYS_MMAN_H
+#define MAP_SHARED 1 /* share changes */
+#define MAP_PRIVATE 2 /* changes are private */
+#define PROT_READ 0x1 /* pages can be read */
+#define PROT_WRITE 0x2 /* pages can be written */
+#define PROT_EXEC 0x4 /* pages can be executed */
+#endif
+
+/*
+ * XXX
+ * POSIX 1003.1 names for file descriptors.
+ */
+#ifndef STDERR_FILENO
+#define STDIN_FILENO 0 /* ANSI C #defines */
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+#endif
+
+/*
+ * XXX
+ * POSIX 1003.1 names for seek settings.
+ */
+#ifndef SEEK_END
+#define SEEK_SET 0 /* POSIX 1003.1 seek values */
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+/*
+ * Hack _POSIX_VDISABLE to \377 since Ultrix doesn't honor _POSIX_VDISABLE
+ * (treats it as ^@). The symptom is that the ^@ keystroke immediately
+ * drops core.
+ */
+#ifdef HAVE_BROKEN_VDISABLE
+#undef _POSIX_VDISABLE
+#define _POSIX_VDISABLE ((unsigned char)'\377')
+#endif
+
+/*
+ * XXX
+ * POSIX 1003.1 tty disabling character.
+ */
+#ifndef _POSIX_VDISABLE
+#define _POSIX_VDISABLE 0 /* Some systems used 0. */
+#endif
+
+/*
+ * XXX
+ * 4.4BSD extension to only set the software termios bits.
+ */
+#ifndef TCSASOFT /* 4.4BSD extension. */
+#define TCSASOFT 0
+#endif
+
+/*
+ * XXX
+ * POSIX 1003.1 maximum path length.
+ */
+#ifndef MAXPATHLEN
+#ifdef PATH_MAX
+#define MAXPATHLEN PATH_MAX
+#else
+#define MAXPATHLEN 1024
+#endif
+#endif
+
+/*
+ * XXX
+ * MIN, MAX, historically in <sys/param.h>
+ */
+#ifndef MAX
+#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a))
+#endif
+#ifndef MIN
+#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
+#endif
+
+/*
+ * XXX
+ * "DB" isn't always portable, and we want the private information.
+ */
+#define DB L__DB
+#undef pgno_t /* IRIX has its own version. */
+#define pgno_t L__db_pgno_t
+
+/*
+ * XXX
+ * 4.4BSD extension to provide lock values in the open(2) call.
+ */
+#ifndef O_EXLOCK
+#define O_EXLOCK 0
+#endif
+
+#ifndef O_SHLOCK
+#define O_SHLOCK 0
+#endif
+
+/*
+ * XXX
+ * POSIX 1003.1 bad file format errno.
+ */
+#ifndef EFTYPE
+#define EFTYPE EINVAL
+#endif
+
+/*
+ * XXX
+ * POSIX 1003.2 RE length limit.
+ */
+#ifndef _POSIX2_RE_DUP_MAX
+#define _POSIX2_RE_DUP_MAX 255
+#endif
+
+/*
+ * XXX
+ * 4.4BSD extension to determine if a program dropped core from the exit
+ * status.
+ */
+#ifndef WCOREDUMP
+#define WCOREDUMP(a) 0
+#endif
+
+/*
+ * XXX
+ * Endian-ness of the machine.
+ */
+#if !defined(LITTLE_ENDIAN)
+#define LITTLE_ENDIAN 1234
+#endif
+#if !defined(BIG_ENDIAN)
+#define BIG_ENDIAN 4321
+#endif
+#if !defined(BYTE_ORDER)
+#if WORDS_BIGENDIAN == 1
+#define BYTE_ORDER BIG_ENDIAN
+#else
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+#endif
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..6adb4e0
--- /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
+.Xr unvis 1 .
+.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.
+.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 appeared in
+.Bx 4.4 .
diff --git a/usr.bin/vis/vis.c b/usr.bin/vis/vis.c
new file mode 100644
index 0000000..93bda20
--- /dev/null
+++ b/usr.bin/vis/vis.c
@@ -0,0 +1,176 @@
+/*-
+ * 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>
+#include <locale.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;
+
+ (void) setlocale(LC_CTYPE, "");
+
+ while ((ch = getopt(argc, argv, "nwctsobfF:ld")) != -1)
+ 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..117c30d
--- /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= ${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..ca6eb41
--- /dev/null
+++ b/usr.bin/vmstat/vmstat.8
@@ -0,0 +1,219 @@
+.\" 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
+.\"
+.Dd June 6, 1996
+.Dt VMSTAT 8
+.Os BSD 4
+.Sh NAME
+.Nm vmstat
+.Nd report virtual memory statistics
+.Sh SYNOPSIS
+.Nm vmstat
+.Op Fl fimst
+.Op Fl c Ar count
+.Op Fl M core
+.Op Fl N system
+.Op Fl w wait
+.Op Ar disks
+.Sh DESCRIPTION
+.Nm Vmstat
+reports certain kernel statistics kept about process, virtual memory,
+disk, trap and cpu activity.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c
+Repeat the display
+.Ar 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
+.Ar wait
+interval is specified, the default is 1 second.
+.\" .It Fl f
+.\" Report on the number
+.\" .Xr fork 2
+.\" and
+.\" .Xr vfork 2
+.\" system calls since system startup, and the number of pages of virtual memory
+.\" involved in each.
+.It Fl i
+Report on the number of interrupts taken by each device since system
+startup.
+.It Fl M
+Extract values associated with the name list from the specified
+.Ar core
+instead of the default
+.Pa /dev/kmem .
+.It Fl N
+Extract the name list from the specified
+.Ar system
+instead of the default
+.Pa /kernel .
+.It Fl m
+Report on the usage of kernel dynamic memory listed first by size of
+allocation and then by type of usage.
+.It Fl s
+Display the contents of the
+.Em sum
+structure, giving the total number of several kinds of paging related
+events which have occurred since system startup.
+.\" .It Fl t
+.\" Report on the number of page in and page reclaims since system startup,
+.\" and the amount of time required by each.
+.It Fl w
+Pause
+.Ar wait
+seconds between each display.
+If no repeat
+.Ar count
+is specified, the default is infinity.
+.El
+.Pp
+By default,
+.Nm vmstat
+displays the following information:
+.Pp
+.Bl -tag -width indent
+.It procs
+Information about the numbers of processes in various states.
+.Pp
+.Bl -tag -width indent -compact
+.It r
+in run queue
+.It b
+blocked for resources (i/o, paging, etc.)
+.It w
+runnable or short sleeper (< 20 secs) but swapped
+.El
+.It 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.
+.Pp
+.Bl -tag -width indent -compact
+.It avm
+active virtual pages
+.It fre
+size of the free list
+.El
+.It page
+Information about page faults and paging activity.
+These are averaged each five seconds, and given in units per second.
+.Pp
+.Bl -tag -width indent -compact
+.It flt
+total number of page faults
+.It re
+page reclaims (simulating reference bits)
+.\" .It at
+.\" pages attached (found in free list)
+.It pi
+pages paged in
+.It po
+pages paged out
+.It fr
+pages freed per second
+.\" .It de
+.\" anticipated short term memory shortfall
+.It sr
+pages scanned by clock algorithm, per-second
+.El
+.It 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,
+.Nm vmstat
+displays only the first four drives.
+To force
+.Nm vmstat
+to display specific drives, their names may be supplied on the command line.
+.It faults
+Trap/interrupt rate averages per second over last 5 seconds.
+.Pp
+.Bl -tag -width indent -compact
+.It in
+device interrupts per interval (including clock interrupts)
+.It sy
+system calls per interval
+.It cs
+cpu context switch rate (switches/interval)
+.El
+.It cpu
+Breakdown of percentage usage of CPU time.
+.Pp
+.Bl -tag -width indent -compact
+.It us
+user time for normal and low priority processes
+.It sy
+system time
+.It id
+cpu idle
+.El
+.El
+.Sh EXAMPLES
+The command:
+.Dl vmstat -w 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
+.Bl -tag -width /dev/kmemxxx -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 nfsstat 1 ,
+.Xr ps 1 ,
+.Xr systat 1 ,
+.Xr iostat 8 ,
+.Xr pstat 8
+.Pp
+The sections starting with ``Interpreting system activity'' in
+.%T "Installing and Operating 4.3BSD" .
+.SH BUGS
+The
+.Fl c
+and
+.Fl 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..fe363a2
--- /dev/null
+++ b/usr.bin/vmstat/vmstat.c
@@ -0,0 +1,837 @@
+/*
+ * 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/dkstat.h>
+#include <sys/buf.h>
+#include <sys/uio.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 <sys/vmmeter.h>
+
+#include <vm/vm_param.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:")) != -1) {
+ 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("%8ld%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-directory\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..7cb4ed0
--- /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 *));
+int 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..c48f6a7
--- /dev/null
+++ b/usr.bin/w/pr_time.c
@@ -0,0 +1,113 @@
+/*-
+ * 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) - 1, fmt, tp);
+ buf[sizeof(buf) - 1] = '\0';
+ (void)printf("%s", buf);
+}
+
+/*
+ * pr_idle --
+ * Display the idle time.
+ */
+int
+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 (days >= 10)
+ return(1);
+ }
+
+ /* 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);
+
+ return(0); /* not idle longer than 9 days */
+}
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..b0a03f6
--- /dev/null
+++ b/usr.bin/w/w.1
@@ -0,0 +1,142 @@
+.\" 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
+.\" $Id$
+.\"
+.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 finger 1 ,
+.Xr ps 1 ,
+.Xr uptime 1 ,
+.Xr who 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..251cde0
--- /dev/null
+++ b/usr.bin/w/w.c
@@ -0,0 +1,458 @@
+/*-
+ * 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 <locale.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, longidle;
+ char *memf, *nlistf, *p, *vis_args, *x;
+ char buf[MAXHOSTNAMELEN], errbuf[256];
+
+ (void) setlocale(LC_ALL, "");
+
+ /* 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)) != -1)
+ 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.. */
+
+ /*
+ * 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_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 (nflag && *p && strcmp(p, "-") && inet_addr(p) == INADDR_NONE) {
+ hp = gethostbyname(p);
+
+ if (hp != NULL) {
+ struct in_addr in;
+
+ memmove(&in, hp->h_addr, sizeof(in));
+ p = inet_ntoa(in);
+ }
+ }
+ 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);
+ longidle=pr_idle(ep->idle);
+ if (longidle)
+ argwidth--;
+ 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) - 1,
+ __CONCAT("%l:%","M%p"), localtime(nowp));
+ buf[sizeof(buf) - 1] = '\0';
+ (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..f2b8a9d
--- /dev/null
+++ b/usr.bin/wall/wall.c
@@ -0,0 +1,218 @@
+/*
+ * 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 <locale.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];
+
+ (void)setlocale(LC_CTYPE, "");
+
+ while ((ch = getopt(argc, argv, "n")) != -1)
+ 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 cnt;
+ register unsigned char ch;
+ 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) {
+ again:
+ if (cnt == 79 || ch == '\n') {
+ for (; cnt < 79; ++cnt)
+ putc(' ', fp);
+ putc('\r', fp);
+ putc('\n', fp);
+ cnt = 0;
+ } else if ( ( (ch & 0x7F) < ' ' /* locale-independent */
+ || (!isprint(ch) && !isspace(ch))
+ )
+ && strchr("\a\f\t\v\b\r\n", ch) == NULL
+ ) {
+ if (ch & 0x80) {
+ ch &= 0x7F;
+ (void)fprintf(fp, "M-");
+ goto again;
+ }
+ putc('^', fp);
+ putc(ch^0x40, fp); /* DEL to ?, others to alpha */
+ } 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/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..f081b6b
--- /dev/null
+++ b/usr.bin/wc/wc.1
@@ -0,0 +1,115 @@
+.\" 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
+.\" $Id$
+.\"
+.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 .
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/usr.bin/wc/wc.c b/usr.bin/wc/wc.c
new file mode 100644
index 0000000..1670f1b
--- /dev/null
+++ b/usr.bin/wc/wc.c
@@ -0,0 +1,241 @@
+/*
+ * 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 const 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
+#if 0
+static const char sccsid[] = "@(#)wc.c 8.1 (Berkeley) 6/6/93";
+#else
+static const char rcsid[] =
+ "$Id: wc.c,v 1.6 1997/02/22 19:57:44 peter Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+u_long tlinect, twordct, tcharct;
+int doline, doword, dochar;
+
+int cnt __P((char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int ch;
+ int errors, total;
+
+ (void) setlocale(LC_CTYPE, "");
+
+ while ((ch = getopt(argc, argv, "lwc")) != -1)
+ 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;
+
+ errors = 0;
+ total = 0;
+ if (!*argv) {
+ if (cnt((char *)NULL) != 0)
+ ++errors;
+ else
+ (void)printf("\n");
+ }
+ else do {
+ if (cnt(*argv) != 0)
+ ++errors;
+ else
+ (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(errors == 0 ? 0 : 1);
+}
+
+int
+cnt(file)
+ char *file;
+{
+ register u_char *p, ch;
+ register short gotsp;
+ register int len;
+ register u_long linect, wordct, charct;
+ struct stat sb;
+ int fd;
+ u_char buf[MAXBSIZE];
+
+ linect = wordct = charct = 0;
+ if (file == NULL) {
+ file = "stdin";
+ fd = STDIN_FILENO;
+ } else {
+ if ((fd = open(file, O_RDONLY, 0)) < 0) {
+ warn("%s: open", file);
+ return (1);
+ }
+ 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) {
+ warn("%s: read", file);
+ (void)close(fd);
+ return (1);
+ }
+ 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 (0);
+ }
+ /*
+ * 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)) {
+ warn("%s: fstat", file);
+ (void)close(fd);
+ return (1);
+ }
+ 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 (0);
+ }
+ }
+ }
+
+ /* Do it the hard way... */
+word: for (gotsp = 1; len = read(fd, buf, MAXBSIZE);) {
+ if (len == -1) {
+ warn("%s: read", file);
+ (void)close(fd);
+ return (1);
+ }
+ /*
+ * 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);
+ return (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: wc [-clw] [files]\n");
+ exit(1);
+}
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..e8bb617
--- /dev/null
+++ b/usr.bin/what/what.1
@@ -0,0 +1,73 @@
+.\" 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
+.\"
+.\" $Id: what.1,v 1.4 1997/02/22 19:57:46 peter Exp $
+.\"
+.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 SEE ALSO
+.Xr ident 1
+.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..b4a4df1
--- /dev/null
+++ b/usr.bin/whereis/Makefile
@@ -0,0 +1,7 @@
+MAN1= whereis.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/whereis.pl ${DESTDIR}${BINDIR}/whereis
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/whereis/whereis.1 b/usr.bin/whereis/whereis.1
index ee90db0..6c5a4f7 100644
--- a/usr.bin/whereis/whereis.1
+++ b/usr.bin/whereis/whereis.1
@@ -29,35 +29,109 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" @(#)whereis.1 8.3 (Berkeley) 4/27/95
+.\" @(#)whereis.1 8.2 (Berkeley) 12/30/93
.\"
-.Dd April 27, 1995
+.\" $Id$
+.\"
+.Dd June 15, 1996
.Dt WHEREIS 1
-.Os BSD 3
+.Os FreeBSD
.Sh NAME
.Nm whereis
.Nd locate programs
.Sh SYNOPSIS
.Nm whereis
-.Op Ar program ...
+.Op Fl bms
+.Op Fl u
+.Op Fl BMS dir ... Fl f
+.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.
+utility checks the standard binary, manual page, and source
+directories for the specified programs, printing out the paths of any
+it finds. The supplied names are first stripped of leading path name
+components, any single trailing extension of the form
+.Ql .ext ,
+and the leading
+.Ql s.
+or trailing
+.Ql ,v
+from a source code control system.
.Pp
-The path searched is the string returned by the
+The default path searched is the string returned by the
.Xr sysctl 8
utility for the
.Dq user.cs_path
-string.
+string, with
+.Pa /usr/libexec
+and the current user's
+.Ev $PATH
+appended. Manual pages are searched by default along the
+.Ev $MANPATH .
+Program sources are located in a list of known standard places,
+including all the subdirectories of
+.Pa /usr/src
+and
+.Pa /usr/ports .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl B
+Specify directories to search for binaries. Requires the
+.Fl f
+option.
+.It Fl M
+Specify directories to search for manual pages. Requires the
+.Fl f
+option.
+.It Fl S
+Specify directories to search for program sources. Requires the
+.Fl f
+option.
+.It Fl b
+Search for binaries.
+.It Fl f
+Delimits the list of directories after the
+.Fl B ,
+.Fl M ,
+or
+.Fl S
+options, and indicates the beginning of the
+.Ar name
+list.
+.It Fl m
+Search for manual pages.
+.It Fl s
+Search for source directories.
+.It Fl u
+Search for
+.Dq unusual
+entries. A file is said to be unusual if it does not have one entry
+of each requested type.
+.El
+.Sh EXAMPLE
+The following finds all utilities under
+.Pa /usr/bin
+that do not have documentation:
+.Dl whereis -m -u /usr/bin/*
.Sh SEE ALSO
+.Xr locate 1 ,
+.Xr man 1 ,
.Xr sysctl 8
-.Sh COMPATIBILITY
-The historic flags and arguments for the
-.Nm whereis
-utility are no longer available in this version.
+.Sh BUGS
+The search for sources is implemented as a quick search as the
+first-level subdirectory of each element of the list of source
+directories first. If this didn't succeed, the utility
+.Xr locate 1
+is requested to do the search in deeper nested subdirectories. This
+might take some time, and will only succeed if the locate database is
+up-to-date.
.Sh HISTORY
The
.Nm whereis
-command appeared in 3.0BSD.
+command appeared in
+.Bx 3.0 .
+This version re-implements the historical
+functionality that was lost in
+.Bx 4.4 .
diff --git a/usr.bin/whereis/whereis.pl b/usr.bin/whereis/whereis.pl
new file mode 100644
index 0000000..12b3997
--- /dev/null
+++ b/usr.bin/whereis/whereis.pl
@@ -0,0 +1,243 @@
+#!/usr/bin/perl
+#
+# Copyright © 1995, 1996 Jörg Wunsch
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (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 -- search for binaries, man pages and source directories.
+#
+# Rewritten from scratch for FreeBSD after the 4.3BSD manual page.
+#
+# $Id$
+#
+
+sub usage
+{
+ print STDERR "usage: $0 [-bms] [-u] [-BMS dir... -f] name ...\n";
+ exit 1;
+}
+
+sub scanopts
+{
+ local($i, $j);
+ arg:
+ while ($ARGV[$i] =~ /^-/) {
+ opt:
+ for ($j = 1; $j < length($ARGV[$i]); $j++) {
+ local($_) = substr($ARGV[$i], $j, 1);
+ local($what, @list);
+ $opt_b++, next opt if /b/;
+ $opt_m++, next opt if /m/;
+ $opt_s++, next opt if /s/;
+ $opt_u++, next opt if /u/;
+ &usage unless /[BMS]/;
+
+ # directory list processing
+ $what = $_; @list = ();
+ push(@list, substr($ARGV[$i], $j+1)) if $j+1 < length($ARGV[$i]);
+ $i++;
+ while ($i <= $#ARGV && $ARGV[$i] !~ /^-/) {
+ push(@list, $ARGV[$i++]);
+ }
+ if ($what eq "B") {@binaries = @list;}
+ elsif ($what eq "M") {@manuals = @list;}
+ elsif ($what eq "S") {@sources = @list;}
+
+ $i++, last arg if $ARGV[$i] =~ /^-f$/;
+ next arg;
+ }
+ $i++;
+ }
+ &usage if $i > $#ARGV;
+
+ while ($ARGV[$i]) {
+ push(@names, $ARGV[$i++]);
+ }
+}
+
+
+sub decolonify
+{
+ local($list) = @_;
+ local($_, @rv);
+ foreach(split(/:/, $list)) {
+ push(@rv, $_);
+ }
+ return @rv;
+}
+
+
+&scanopts;
+
+# default to all if no type requested
+if ($opt_b + $opt_m + $opt_s == 0) {$opt_b = $opt_m = $opt_s = 1;}
+
+if (!defined(@binaries)) {
+ #
+ # first, use default path, then append /usr/libexec and the user's path
+ #
+ local($cs_path) = `/usr/sbin/sysctl -n user.cs_path`;
+ local(@list, %path);
+
+ chop($cs_path);
+
+ @list = &decolonify($cs_path);
+ push(@list, "/usr/libexec");
+ push(@list, &decolonify($ENV{'PATH'}));
+
+ # resolve ~, remove duplicates
+ foreach (@list) {
+ s/^~/$ENV{'HOME'}/ if /^~/;
+ push(@binaries, $_) if !$path{$_};
+ $path{$_}++;
+ }
+}
+
+if (!defined(@manuals)) {
+ #
+ # first, use default manpath, then append user's $MANPATH
+ #
+ local($usermanpath) = $ENV{'MANPATH'};
+ delete $ENV{'MANPATH'};
+ local($manpath) = `/usr/bin/manpath`;
+ local(@list, %path, $i);
+
+ chop($manpath);
+
+ @list = &decolonify($manpath);
+ push(@list, &decolonify($usermanpath));
+
+ # remove duplicates
+ foreach (@list) {
+ push(@manuals, $_) if !$path{$_};
+ $path{$_}++;
+ }
+}
+
+if (!defined(@sources)) {
+ #
+ # default command sources
+ #
+ local($_);
+
+ @sources = ("/usr/src/bin", "/usr/src/usr.bin", "/usr/src/sbin",
+ "/usr/src/usr.sbin", "/usr/src/libexec",
+ "/usr/src/gnu/bin", "/usr/src/gnu/usr.bin",
+ "/usr/src/gnu/sbin", "/usr/src/gnu/usr.sbin",
+ "/usr/src/gnu/libexec");
+
+ #
+ # if /usr/ports exists, look in all its subdirs, too
+ #
+ if (-d "/usr/ports" && opendir(PORTS, "/usr/ports")) {
+ while ($_ = readdir(PORTS)) {
+ next if /^\.\.?$/;
+ next if /^distfiles$/; # magic
+ next if ! -d "/usr/ports/$_";
+ push(@sources, "/usr/ports/$_");
+ }
+ closedir(PORTS);
+ }
+}
+
+if ($opt_m) {
+ # construct a new MANPATH
+ foreach (@manuals) {
+ next if ! -d $_;
+ if ($manpath) { $manpath .= ":$_"; }
+ else { $manpath = $_; }
+ }
+}
+
+#
+# main loop
+#
+foreach $name (@names) {
+ $name =~ s|^.*/||; # strip leading path name component
+ $name =~ s/,v$//; $name =~ s/^s\.//; # RCS or SCCS suffix/prefix
+ $name =~ s/\.(Z|z|gz)$//; # compression suffix
+ $name =~ s/\.[^.]+//; # any other suffix
+
+ $line = "";
+ $unusual = 0;
+
+ if ($opt_b) {
+ #
+ # Binaries have to match exactly, and must be regular executable
+ # files.
+ #
+ $unusual++;
+ foreach (@binaries) {
+ $line .= " $_/$name", $unusual--, last if -f "$_/$name" && -x _;
+ }
+ }
+
+ if ($opt_m) {
+ #
+ # Ask the man command to do the search for us.
+ #
+ $unusual++;
+ chop($result = `man -S 1:8 -M $manpath -w $name 2> /dev/null`);
+ if ($result ne '') {
+ $unusual--;
+ ($cat, $junk, $src) = split(/[() \t\n]+/, $result);
+ if ($src ne '') { $line .= " $src"; }
+ else { $line .= " $cat"; }
+ }
+ }
+
+ if ($opt_s) {
+ #
+ # Sources match if a subdir with the exact name is found.
+ #
+ $found = 0;
+ $unusual++;
+ foreach (@sources) {
+ $line .= " $_/$name", $unusual--, $found++, last if -d "$_/$name";
+ }
+ #
+ # If not yet found, ask locate(1) to do the search for us.
+ # This will find sources for things like lpr, but take longer.
+ # Do only match locate output that starts with one of our
+ # source directories, and at least one further level of
+ # subdirectories.
+ #
+ if (!$found && open(LOCATE, "locate */$name 2>/dev/null |")) {
+ locate_item:
+ while (chop($loc = <LOCATE>)) {
+ foreach (@sources) {
+ $line .= " $loc", $unusual--, last locate_item
+ if $loc =~ m|^$_/[^/]+/|;
+ }
+ }
+ close(LOCATE);
+ }
+ }
+
+ if ($opt_u) {
+ print "$name:\n" if $unusual;
+ } else {
+ print "$name:$line\n";
+ }
+}
+
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..e62c91e
--- /dev/null
+++ b/usr.bin/which/which.1
@@ -0,0 +1,65 @@
+.\" 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$
+.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
+.Fx 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@FreeBSD.org>.
diff --git a/usr.bin/which/which.pl b/usr.bin/which/which.pl
new file mode 100755
index 0000000..991e551
--- /dev/null
+++ b/usr.bin/which/which.pl
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+#
+# Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED 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 OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (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$
+
+$all = $silent = $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/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..27dc5cd
--- /dev/null
+++ b/usr.bin/who/who.1
@@ -0,0 +1,106 @@
+.\" 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
+.\" $Id$
+.\"
+.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 v1 .
diff --git a/usr.bin/who/who.c b/usr.bin/who/who.c
new file mode 100644
index 0000000..e16a7b9
--- /dev/null
+++ b/usr.bin/who/who.c
@@ -0,0 +1,139 @@
+/*
+ * 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>
+#include <locale.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();
+
+ (void) setlocale(LC_TIME, "");
+
+ 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));
+ buf[sizeof(buf) - 1] = '\0';
+ (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..3b6e7aa
--- /dev/null
+++ b/usr.bin/whois/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= whois
+
+.if defined(SOCKS)
+CFLAGS+=-DSOCKS
+CFLAGS+=-Dconnect=Rconnect -Dgetsockname=Rgetsockname -Dlisten=Rlisten \
+ -Daccept=Raccept -Drcmd=Rrcmd -Dbind=Rbind -Dselect=Rselect
+LDADD+= -lsocks
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/whois/whois.1 b/usr.bin/whois/whois.1
new file mode 100644
index 0000000..6a974b4
--- /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 (whois.internic.net).
+.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..a70301a
--- /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;
+
+#ifdef SOCKS
+ SOCKSinit(argv[0]);
+#endif
+
+ host = NICHOST;
+ while ((ch = getopt(argc, argv, "h:")) != -1)
+ 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;
+ 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..6f32797
--- /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 (see
+.Xr pty 4 )
+or a
+.Ux
+domain socket (see
+.Xr socketpair 2 ) .
+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 (see
+.Xr environ 7 )
+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..16cb704
--- /dev/null
+++ b/usr.bin/write/write.1
@@ -0,0 +1,104 @@
+.\" 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
+.\" $Id: write.1,v 1.4 1997/02/22 19:57:53 peter Exp $
+.\"
+.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.
+.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 v1 .
diff --git a/usr.bin/write/write.c b/usr.bin/write/write.c
new file mode 100644
index 0000000..dd0248d
--- /dev/null
+++ b/usr.bin/write/write.c
@@ -0,0 +1,331 @@
+/*
+ * 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 <paths.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], _PATH_DEV, strlen(_PATH_DEV)))
+ argv[2] += strlen(_PATH_DEV);
+ 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)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, 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)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, 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 unsigned char *s;
+{
+
+#define PUTC(c) if (putchar(c) == EOF) goto err;
+
+ for (; *s != '\0'; ++s) {
+ if (*s == '\n') {
+ PUTC('\r');
+ } else if (!isprint(*s) && !isspace(*s) && *s != '\007') {
+ if (*s & 0x80) {
+ *s &= ~0x80;
+ PUTC('M');
+ PUTC('-');
+ }
+ if (iscntrl(*s)) {
+ *s ^= 0x40;
+ PUTC('^');
+ }
+ }
+ PUTC(*s);
+ }
+ 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..ab10647
--- /dev/null
+++ b/usr.bin/xargs/xargs.1
@@ -0,0 +1,172 @@
+.\" 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 0
+.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 0
+Changes
+.Nm xargs
+to expect NUL
+(``\\0'')
+characters as seperators, instead of spaces and newlines.
+This is expected to be used in concert with the
+.Fl print0
+function in
+.Nm find .
+.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..a0e8c0d
--- /dev/null
+++ b/usr.bin/xargs/xargs.c
@@ -0,0 +1,342 @@
+/*-
+ * 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;
+int zflag;
+
+void err __P((const char *, ...));
+void run __P((char **));
+void usage __P((void));
+
+main(argc, argv, env)
+ int argc;
+ char **argv, **env;
+{
+ register int ch;
+ register char *p, *bbp, *ebp, **bxp, **exp, **xp;
+ int cnt, indouble, insingle, nargs, nflag, nline, xflag;
+ char **av, *argp, **ep = env;
+
+ /*
+ * 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;
+ while (*ep) {
+ /* 1 byte for each '\0' */
+ nline -= strlen(*ep++) + 1 + sizeof(*ep);
+ }
+ nflag = xflag = 0;
+ while ((ch = getopt(argc, argv, "0n:s:tx")) != -1)
+ 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 '0':
+ zflag = 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 || zflag)
+ goto addch;
+ goto arg2;
+ case '\0':
+ if (zflag)
+ goto arg2;
+ goto addch;
+ case '\n':
+ if (zflag)
+ goto addch;
+
+ /* 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 || zflag)
+ goto addch;
+ insingle = !insingle;
+ break;
+ case '"':
+ if (insingle || zflag)
+ goto addch;
+ indouble = !indouble;
+ break;
+ case '\\':
+ if (zflag)
+ goto addch;
+ /* 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 [-0] [-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..71cc4b8
--- /dev/null
+++ b/usr.bin/xinstall/install.1
@@ -0,0 +1,175 @@
+.\" 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 September 22, 1996
+.Dt INSTALL 1
+.Os BSD 4.2
+.Sh NAME
+.Nm install
+.Nd install binaries
+.Sh SYNOPSIS
+.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 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
+.Nm install
+.Fl d
+.Op Fl g Ar group
+.Op Fl m Ar mode
+.Op Fl o Ar owner
+.Ar 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 specified,
+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 d
+Create directories.
+Missing parent directories are created as required.
+.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. A numeric GID is allowed.
+.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. A numeric UID is allowed.
+.It Fl p
+Preserve the modification time.
+Copy the file, as if the
+.Fl C
+(Compare and copy) option is specified,
+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..d809f2c
--- /dev/null
+++ b/usr.bin/xinstall/xinstall.c
@@ -0,0 +1,711 @@
+/*
+ * 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: xinstall.c,v 1.22 1997/03/29 04:34:07 imp Exp $";
+#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 <sys/mount.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 <unistd.h>
+#include <sysexits.h>
+#include <utime.h>
+
+#include "pathnames.h"
+
+/* Bootstrap aid - this doesn't exist in most older releases */
+#ifndef MAP_FAILED
+#define MAP_FAILED ((caddr_t)-1) /* from <sys/mman.h> */
+#endif
+
+int debug, docompare, docopy, dodir, dopreserve, dostrip, verbose;
+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));
+void install_dir __P((char *));
+u_long string_to_flags __P((char **, u_long *, u_long *));
+void strip __P((char *));
+void usage __P((void));
+int trymmap __P((int));
+
+#define ALLOW_NUMERIC_IDS 1
+#ifdef ALLOW_NUMERIC_IDS
+
+uid_t uid;
+gid_t gid;
+
+uid_t resolve_uid __P((char *));
+gid_t resolve_gid __P((char *));
+u_long numeric_id __P((char *, char *));
+
+#else
+
+struct passwd *pp;
+struct group *gp;
+
+#endif /* ALLOW_NUMERIC_IDS */
+
+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, "CcdDf:g:m:o:psv")) != -1)
+ switch((char)ch) {
+ case 'C':
+ docompare = docopy = 1;
+ break;
+ case 'c':
+ docopy = 1;
+ break;
+ case 'D':
+ debug++;
+ break;
+ case 'd':
+ dodir = 1;
+ 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 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* some options make no sense when creating directories */
+ if ((docompare || dostrip) && dodir)
+ usage();
+
+ /* must have at least two arguments, except when creating directories */
+ if (argc < 2 && !dodir)
+ usage();
+
+#ifdef ALLOW_NUMERIC_IDS
+
+ if (owner)
+ uid = resolve_uid(owner);
+ if (group)
+ gid = resolve_gid(group);
+
+#else
+
+ /* get group and owner id's */
+ if (owner && !(pp = getpwnam(owner)))
+ errx(EX_NOUSER, "unknown user %s", owner);
+ if (group && !(gp = getgrnam(group)))
+ errx(EX_NOUSER, "unknown group %s", group);
+
+#endif /* ALLOW_NUMERIC_IDS */
+
+ if (dodir) {
+ for (; *argv != NULL; ++argv)
+ install_dir(*argv);
+ exit(EX_OK);
+ /* NOTREACHED */
+ }
+
+ 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(EX_OK);
+ /* NOTREACHED */
+ }
+
+ /* 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(EX_OK);
+ /* NOTREACHED */
+}
+
+#ifdef ALLOW_NUMERIC_IDS
+
+uid_t
+resolve_uid(s)
+ char *s;
+{
+ struct passwd *pw;
+
+ return ((pw = getpwnam(s)) == NULL) ?
+ (uid_t) numeric_id(s, "user") : pw->pw_uid;
+}
+
+gid_t
+resolve_gid(s)
+ char *s;
+{
+ struct group *gr;
+
+ return ((gr = getgrnam(s)) == NULL) ?
+ (gid_t) numeric_id(s, "group") : gr->gr_gid;
+}
+
+u_long
+numeric_id(name, type)
+ char *name, *type;
+{
+ u_long val;
+ char *ep;
+
+ /*
+ * XXX
+ * We know that uid_t's and gid_t's are unsigned longs.
+ */
+ errno = 0;
+ val = strtoul(name, &ep, 10);
+ if (errno)
+ err(EX_NOUSER, "%s", name);
+ if (*ep != '\0')
+ errx(EX_NOUSER, "unknown %s %s", type, name);
+ return (val);
+}
+
+#endif /* ALLOW_NUMERIC_IDS */
+
+/*
+ * 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 (verbose) {
+ printf("install: %s -> %s\n",
+ from_name, old_to_name);
+ }
+ 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) &&
+#ifdef ALLOW_NUMERIC_IDS
+ fchown(to_fd, owner ? uid : -1, group ? gid : -1)) {
+#else
+ fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) {
+#endif
+ 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;
+ int done_compare;
+
+ if (from_sb->st_size != to_sb->st_size)
+ return 1;
+
+ tsize = (size_t)from_sb->st_size;
+
+ if (tsize <= 8 * 1024 * 1024) {
+ done_compare = 0;
+ if (trymmap(from_fd) && trymmap(to_fd)) {
+ p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
+ if (p == (char *)MAP_FAILED)
+ goto out;
+ q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
+ if (q == (char *)MAP_FAILED) {
+ munmap(p, tsize);
+ goto out;
+ }
+
+ rv = memcmp(p, q, tsize);
+ munmap(p, tsize);
+ munmap(q, tsize);
+ done_compare = 1;
+ }
+ out:
+ if (!done_compare) {
+ char buf1[MAXBSIZE];
+ char buf2[MAXBSIZE];
+ int n1, n2;
+
+ rv = 0;
+ lseek(from_fd, 0, SEEK_SET);
+ lseek(to_fd, 0, SEEK_SET);
+ while (rv == 0) {
+ n1 = read(from_fd, buf1, sizeof(buf1));
+ if (n1 == 0)
+ break; /* EOF */
+ else if (n1 > 0) {
+ n2 = read(to_fd, buf2, n1);
+ if (n2 == n1)
+ rv = memcmp(buf1, buf2, n1);
+ else
+ rv = 1; /* out of sync */
+ } else
+ rv = 1; /* read failure */
+ }
+ lseek(from_fd, 0, SEEK_SET);
+ lseek(to_fd, 0, SEEK_SET);
+ }
+ } 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];
+ int done_copy;
+
+ /*
+ * 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.
+ */
+ done_copy = 0;
+ if (size <= 8 * 1048576 && trymmap(from_fd)) {
+ if ((p = mmap(NULL, (size_t)size, PROT_READ,
+ MAP_SHARED, from_fd, (off_t)0)) == (char *)MAP_FAILED)
+ goto out;
+ if ((nw = write(to_fd, p, size)) != size) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = nw > 0 ? EIO : serrno;
+ err(EX_OSERR, "%s", to_name);
+ }
+ done_copy = 1;
+ out:
+ }
+ if (!done_copy) {
+ 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:
+ execlp("strip", "strip", to_name, NULL);
+ err(EX_OSERR, "exec(strip)");
+ default:
+ if (wait(&status) == -1 || status) {
+ (void)unlink(to_name);
+ exit(EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ }
+}
+
+/*
+ * install_dir --
+ * build directory heirarchy
+ */
+void
+install_dir(path)
+ char *path;
+{
+ register char *p;
+ struct stat sb;
+ int ch;
+
+ for (p = path;; ++p)
+ if (!*p || (p != path && *p == '/')) {
+ ch = *p;
+ *p = '\0';
+ if (stat(path, &sb)) {
+ if (errno != ENOENT || mkdir(path, 0777) < 0) {
+ err(EX_OSERR, "%s", path);
+ /* NOTREACHED */
+ }
+ }
+ if (!(*p = ch))
+ break;
+ }
+
+ if (((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) ||
+ chmod(path, mode)) {
+ warn("%s", path);
+ }
+}
+
+/*
+ * 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\
+ install [-CcDps] [-f flags] [-g group] [-m mode] [-o owner] file1 ...\n\
+ fileN directory\n\
+ install -d [-g group] [-m mode] [-o owner] directory ...\n");
+ exit(EX_USAGE);
+ /* NOTREACHED */
+}
+
+/*
+ * trymmap --
+ * return true (1) if mmap should be tried, false (0) if not.
+ */
+int
+trymmap(fd)
+ int fd;
+{
+ struct statfs stfs;
+
+ if (fstatfs(fd, &stfs) < 0)
+ return 0;
+ switch(stfs.f_type) {
+ case MOUNT_UFS: /* should be safe.. */
+ case MOUNT_CD9660: /* should be safe.. */
+ return 1;
+ }
+ return 0;
+}
diff --git a/usr.bin/xlint/Makefile b/usr.bin/xlint/Makefile
new file mode 100644
index 0000000..69338b7
--- /dev/null
+++ b/usr.bin/xlint/Makefile
@@ -0,0 +1,5 @@
+# $NetBSD: Makefile,v 1.2 1995/07/03 21:23:45 cgd Exp $
+
+SUBDIR= lint1 lint2 xlint #llib
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/xlint/lint1/Makefile b/usr.bin/xlint/lint1/Makefile
new file mode 100644
index 0000000..d5e519a
--- /dev/null
+++ b/usr.bin/xlint/lint1/Makefile
@@ -0,0 +1,19 @@
+# $NetBSD: Makefile,v 1.3 1995/07/04 01:53:05 cgd Exp $
+
+PROG= lint1
+SRCS= cgram.c scan.c mem1.c mem.c err.c main1.c decl.c tree.c func.c \
+ init.c emit.c emit1.c
+NOMAN=
+LDADD+= -ll
+DPADD+= ${LIBL}
+YFLAGS= -d
+CFLAGS+=-I.
+LINTFLAGS=-aehpz
+CLEANFILES+=y.tab.h cgram.c scan.c
+
+BINDIR= /usr/libexec
+
+# XXX: -O causes the gcc to die on the i386, when compiling tree.o
+CFLAGS+= -DXXX_BROKEN_GCC
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/xlint/lint1/cgram.y b/usr.bin/xlint/lint1/cgram.y
new file mode 100644
index 0000000..6be2089
--- /dev/null
+++ b/usr.bin/xlint/lint1/cgram.y
@@ -0,0 +1,1686 @@
+%{
+/* $NetBSD: cgram.y,v 1.8 1995/10/02 17:31:35 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: cgram.y,v 1.8 1995/10/02 17:31:35 jpo Exp $";
+#endif
+
+#include <stdlib.h>
+#include <limits.h>
+
+#include "lint1.h"
+
+/*
+ * Contains the level of current declaration. 0 is extern.
+ * Used for symbol table entries.
+ */
+int blklev;
+
+/*
+ * level for memory allocation. Normaly the same as blklev.
+ * An exeption is the declaration of arguments in prototypes. Memory
+ * for these can't be freed after the declaration, but symbols must
+ * be removed from the symbol table after the declaration.
+ */
+int mblklev;
+
+static int toicon __P((tnode_t *));
+static void idecl __P((sym_t *, int));
+static void ignuptorp __P((void));
+
+%}
+
+%union {
+ int y_int;
+ val_t *y_val;
+ sbuf_t *y_sb;
+ sym_t *y_sym;
+ op_t y_op;
+ scl_t y_scl;
+ tspec_t y_tspec;
+ tqual_t y_tqual;
+ type_t *y_type;
+ tnode_t *y_tnode;
+ strg_t *y_strg;
+ pqinf_t *y_pqinf;
+};
+
+%token T_LBRACE T_RBRACE T_LBRACK T_RBRACK T_LPARN T_RPARN
+%token <y_op> T_STROP
+%token <y_op> T_UNOP
+%token <y_op> T_INCDEC
+%token T_SIZEOF
+%token <y_op> T_MULT
+%token <y_op> T_DIVOP
+%token <y_op> T_ADDOP
+%token <y_op> T_SHFTOP
+%token <y_op> T_RELOP
+%token <y_op> T_EQOP
+%token <y_op> T_AND
+%token <y_op> T_XOR
+%token <y_op> T_OR
+%token <y_op> T_LOGAND
+%token <y_op> T_LOGOR
+%token T_QUEST
+%token T_COLON
+%token <y_op> T_ASSIGN
+%token <y_op> T_OPASS
+%token T_COMMA
+%token T_SEMI
+%token T_ELLIPSE
+
+/* storage classes (extern, static, auto, register and typedef) */
+%token <y_scl> T_SCLASS
+
+/* types (char, int, short, long, unsigned, signed, float, double, void) */
+%token <y_tspec> T_TYPE
+
+/* qualifiers (const, volatile) */
+%token <y_tqual> T_QUAL
+
+/* struct or union */
+%token <y_tspec> T_SOU
+
+/* enum */
+%token T_ENUM
+
+/* remaining keywords */
+%token T_CASE
+%token T_DEFAULT
+%token T_IF
+%token T_ELSE
+%token T_SWITCH
+%token T_DO
+%token T_WHILE
+%token T_FOR
+%token T_GOTO
+%token T_CONTINUE
+%token T_BREAK
+%token T_RETURN
+%token T_ASM
+
+%left T_COMMA
+%right T_ASSIGN T_OPASS
+%right T_QUEST T_COLON
+%left T_LOGOR
+%left T_LOGAND
+%left T_OR
+%left T_XOR
+%left T_AND
+%left T_EQOP
+%left T_RELOP
+%left T_SHFTOP
+%left T_ADDOP
+%left T_MULT T_DIVOP
+%right T_UNOP T_INCDEC T_SIZEOF
+%left T_LPARN T_LBRACK T_STROP
+
+%token <y_sb> T_NAME
+%token <y_sb> T_TYPENAME
+%token <y_val> T_CON
+%token <y_strg> T_STRING
+
+%type <y_sym> func_decl
+%type <y_sym> notype_decl
+%type <y_sym> type_decl
+%type <y_type> typespec
+%type <y_type> clrtyp_typespec
+%type <y_type> notype_typespec
+%type <y_type> struct_spec
+%type <y_type> enum_spec
+%type <y_sym> struct_tag
+%type <y_sym> enum_tag
+%type <y_tspec> struct
+%type <y_sym> struct_declaration
+%type <y_sb> identifier
+%type <y_sym> member_declaration_list_with_rbrace
+%type <y_sym> member_declaration_list
+%type <y_sym> member_declaration
+%type <y_sym> notype_member_decls
+%type <y_sym> type_member_decls
+%type <y_sym> notype_member_decl
+%type <y_sym> type_member_decl
+%type <y_tnode> constant
+%type <y_sym> enum_declaration
+%type <y_sym> enums_with_opt_comma
+%type <y_sym> enums
+%type <y_sym> enumerator
+%type <y_sym> ename
+%type <y_sym> notype_direct_decl
+%type <y_sym> type_direct_decl
+%type <y_pqinf> pointer
+%type <y_pqinf> asterisk
+%type <y_sym> param_decl
+%type <y_sym> param_list
+%type <y_sym> abs_decl_param_list
+%type <y_sym> direct_param_decl
+%type <y_sym> notype_param_decl
+%type <y_sym> direct_notype_param_decl
+%type <y_pqinf> type_qualifier_list
+%type <y_pqinf> type_qualifier
+%type <y_sym> identifier_list
+%type <y_sym> abs_decl
+%type <y_sym> direct_abs_decl
+%type <y_sym> vararg_parameter_type_list
+%type <y_sym> parameter_type_list
+%type <y_sym> parameter_declaration
+%type <y_tnode> expr
+%type <y_tnode> term
+%type <y_tnode> func_arg_list
+%type <y_op> point_or_arrow
+%type <y_type> type_name
+%type <y_sym> abstract_declaration
+%type <y_tnode> do_while_expr
+%type <y_tnode> opt_expr
+%type <y_strg> string
+%type <y_strg> string2
+
+
+%%
+
+program:
+ /* empty */ {
+ if (sflag) {
+ /* empty translation unit */
+ error(272);
+ } else if (!tflag) {
+ /* empty translation unit */
+ warning(272);
+ }
+ }
+ | translation_unit
+ ;
+
+translation_unit:
+ ext_decl
+ | translation_unit ext_decl
+ ;
+
+ext_decl:
+ func_def {
+ glclup(0);
+ clrwflgs();
+ }
+ | data_def {
+ glclup(0);
+ clrwflgs();
+ }
+ ;
+
+data_def:
+ T_SEMI {
+ if (sflag) {
+ /* syntax error: empty declaration */
+ error(0);
+ } else if (!tflag) {
+ /* syntax error: empty declaration */
+ warning(0);
+ }
+ }
+ | clrtyp deftyp notype_init_decls T_SEMI {
+ if (sflag) {
+ /* old style declaration; add "int" */
+ error(1);
+ } else if (!tflag) {
+ /* old style declaration; add "int" */
+ warning(1);
+ }
+ }
+ | declmods deftyp T_SEMI {
+ if (dcs->d_scl == TYPEDEF) {
+ /* typedef declares no type name */
+ warning(72);
+ } else {
+ /* empty declaration */
+ warning(2);
+ }
+ }
+ | declmods deftyp notype_init_decls T_SEMI
+ | declspecs deftyp T_SEMI {
+ if (dcs->d_scl == TYPEDEF) {
+ /* typedef declares no type name */
+ warning(72);
+ } else if (!dcs->d_nedecl) {
+ /* empty declaration */
+ warning(2);
+ }
+ }
+ | declspecs deftyp type_init_decls T_SEMI
+ | error T_SEMI {
+ globclup();
+ }
+ | error T_RBRACE {
+ globclup();
+ }
+ ;
+
+func_def:
+ func_decl {
+ if ($1->s_type->t_tspec != FUNC) {
+ /* syntax error */
+ error(249);
+ YYERROR;
+ }
+ if ($1->s_type->t_typedef) {
+ /* ()-less function definition */
+ error(64);
+ YYERROR;
+ }
+ funcdef($1);
+ blklev++;
+ pushdecl(ARG);
+ } opt_arg_declaration_list {
+ popdecl();
+ blklev--;
+ cluparg();
+ pushctrl(0);
+ } comp_stmnt {
+ funcend();
+ popctrl(0);
+ }
+ ;
+
+func_decl:
+ clrtyp deftyp notype_decl {
+ $$ = $3;
+ }
+ | declmods deftyp notype_decl {
+ $$ = $3;
+ }
+ | declspecs deftyp type_decl {
+ $$ = $3;
+ }
+ ;
+
+opt_arg_declaration_list:
+ /* empty */
+ | arg_declaration_list
+ ;
+
+arg_declaration_list:
+ arg_declaration
+ | arg_declaration_list arg_declaration
+ /* XXX or better "arg_declaration error" ? */
+ | error
+ ;
+
+/*
+ * "arg_declaration" is separated from "declaration" because it
+ * needs other error handling.
+ */
+
+arg_declaration:
+ declmods deftyp T_SEMI {
+ /* empty declaration */
+ warning(2);
+ }
+ | declmods deftyp notype_init_decls T_SEMI
+ | declspecs deftyp T_SEMI {
+ if (!dcs->d_nedecl) {
+ /* empty declaration */
+ warning(2);
+ } else {
+ tspec_t ts = dcs->d_type->t_tspec;
+ /* %s declared in argument declaration list */
+ warning(3, ts == STRUCT ? "struct" :
+ (ts == UNION ? "union" : "enum"));
+ }
+ }
+ | declspecs deftyp type_init_decls T_SEMI {
+ if (dcs->d_nedecl) {
+ tspec_t ts = dcs->d_type->t_tspec;
+ /* %s declared in argument declaration list */
+ warning(3, ts == STRUCT ? "struct" :
+ (ts == UNION ? "union" : "enum"));
+ }
+ }
+ | declmods error
+ | declspecs error
+ ;
+
+declaration:
+ declmods deftyp T_SEMI {
+ if (dcs->d_scl == TYPEDEF) {
+ /* typedef declares no type name */
+ warning(72);
+ } else {
+ /* empty declaration */
+ warning(2);
+ }
+ }
+ | declmods deftyp notype_init_decls T_SEMI
+ | declspecs deftyp T_SEMI {
+ if (dcs->d_scl == TYPEDEF) {
+ /* typedef declares no type name */
+ warning(72);
+ } else if (!dcs->d_nedecl) {
+ /* empty declaration */
+ warning(2);
+ }
+ }
+ | declspecs deftyp type_init_decls T_SEMI
+ | error T_SEMI
+ ;
+
+clrtyp:
+ {
+ clrtyp();
+ }
+ ;
+
+deftyp:
+ /* empty */ {
+ deftyp();
+ }
+ ;
+
+declspecs:
+ clrtyp_typespec {
+ addtype($1);
+ }
+ | declmods typespec {
+ addtype($2);
+ }
+ | declspecs declmod
+ | declspecs notype_typespec {
+ addtype($2);
+ }
+ ;
+
+declmods:
+ clrtyp T_QUAL {
+ addqual($2);
+ }
+ | clrtyp T_SCLASS {
+ addscl($2);
+ }
+ | declmods declmod
+ ;
+
+declmod:
+ T_QUAL {
+ addqual($1);
+ }
+ | T_SCLASS {
+ addscl($1);
+ }
+ ;
+
+clrtyp_typespec:
+ clrtyp notype_typespec {
+ $$ = $2;
+ }
+ | T_TYPENAME clrtyp {
+ $$ = getsym($1)->s_type;
+ }
+ ;
+
+typespec:
+ notype_typespec {
+ $$ = $1;
+ }
+ | T_TYPENAME {
+ $$ = getsym($1)->s_type;
+ }
+ ;
+
+notype_typespec:
+ T_TYPE {
+ $$ = gettyp($1);
+ }
+ | struct_spec {
+ popdecl();
+ $$ = $1;
+ }
+ | enum_spec {
+ popdecl();
+ $$ = $1;
+ }
+ ;
+
+struct_spec:
+ struct struct_tag {
+ /*
+ * STDC requires that "struct a;" always introduces
+ * a new tag if "a" is not declared at current level
+ *
+ * yychar is valid because otherwise the parser would
+ * not been able to deceide if he must shift or reduce
+ */
+ $$ = mktag($2, $1, 0, yychar == T_SEMI);
+ }
+ | struct struct_tag {
+ dcs->d_tagtyp = mktag($2, $1, 1, 0);
+ } struct_declaration {
+ $$ = compltag(dcs->d_tagtyp, $4);
+ }
+ | struct {
+ dcs->d_tagtyp = mktag(NULL, $1, 1, 0);
+ } struct_declaration {
+ $$ = compltag(dcs->d_tagtyp, $3);
+ }
+ | struct error {
+ symtyp = FVFT;
+ $$ = gettyp(INT);
+ }
+ ;
+
+struct:
+ T_SOU {
+ symtyp = FTAG;
+ pushdecl($1 == STRUCT ? MOS : MOU);
+ dcs->d_offset = 0;
+ dcs->d_stralign = CHAR_BIT;
+ $$ = $1;
+ }
+ ;
+
+struct_tag:
+ identifier {
+ $$ = getsym($1);
+ }
+ ;
+
+struct_declaration:
+ struct_decl_lbrace member_declaration_list_with_rbrace {
+ $$ = $2;
+ }
+ ;
+
+struct_decl_lbrace:
+ T_LBRACE {
+ symtyp = FVFT;
+ }
+ ;
+
+member_declaration_list_with_rbrace:
+ member_declaration_list T_SEMI T_RBRACE {
+ $$ = $1;
+ }
+ | member_declaration_list T_RBRACE {
+ if (sflag) {
+ /* syntax req. ";" after last struct/union member */
+ error(66);
+ } else {
+ /* syntax req. ";" after last struct/union member */
+ warning(66);
+ }
+ $$ = $1;
+ }
+ | T_RBRACE {
+ $$ = NULL;
+ }
+ ;
+
+member_declaration_list:
+ member_declaration {
+ $$ = $1;
+ }
+ | member_declaration_list T_SEMI member_declaration {
+ $$ = lnklst($1, $3);
+ }
+ ;
+
+member_declaration:
+ noclass_declmods deftyp {
+ /* too late, i know, but getsym() compensates it */
+ symtyp = FMOS;
+ } notype_member_decls {
+ symtyp = FVFT;
+ $$ = $4;
+ }
+ | noclass_declspecs deftyp {
+ symtyp = FMOS;
+ } type_member_decls {
+ symtyp = FVFT;
+ $$ = $4;
+ }
+ | noclass_declmods deftyp {
+ /* struct or union member must be named */
+ warning(49);
+ $$ = NULL;
+ }
+ | noclass_declspecs deftyp {
+ /* struct or union member must be named */
+ warning(49);
+ $$ = NULL;
+ }
+ | error {
+ symtyp = FVFT;
+ $$ = NULL;
+ }
+ ;
+
+noclass_declspecs:
+ clrtyp_typespec {
+ addtype($1);
+ }
+ | noclass_declmods typespec {
+ addtype($2);
+ }
+ | noclass_declspecs T_QUAL {
+ addqual($2);
+ }
+ | noclass_declspecs notype_typespec {
+ addtype($2);
+ }
+ ;
+
+noclass_declmods:
+ clrtyp T_QUAL {
+ addqual($2);
+ }
+ | noclass_declmods T_QUAL {
+ addqual($2);
+ }
+ ;
+
+notype_member_decls:
+ notype_member_decl {
+ $$ = decl1str($1);
+ }
+ | notype_member_decls {
+ symtyp = FMOS;
+ } T_COMMA type_member_decl {
+ $$ = lnklst($1, decl1str($4));
+ }
+ ;
+
+type_member_decls:
+ type_member_decl {
+ $$ = decl1str($1);
+ }
+ | type_member_decls {
+ symtyp = FMOS;
+ } T_COMMA type_member_decl {
+ $$ = lnklst($1, decl1str($4));
+ }
+ ;
+
+notype_member_decl:
+ notype_decl {
+ $$ = $1;
+ }
+ | notype_decl T_COLON constant {
+ $$ = bitfield($1, toicon($3));
+ }
+ | {
+ symtyp = FVFT;
+ } T_COLON constant {
+ $$ = bitfield(NULL, toicon($3));
+ }
+ ;
+
+type_member_decl:
+ type_decl {
+ $$ = $1;
+ }
+ | type_decl T_COLON constant {
+ $$ = bitfield($1, toicon($3));
+ }
+ | {
+ symtyp = FVFT;
+ } T_COLON constant {
+ $$ = bitfield(NULL, toicon($3));
+ }
+ ;
+
+enum_spec:
+ enum enum_tag {
+ $$ = mktag($2, ENUM, 0, 0);
+ }
+ | enum enum_tag {
+ dcs->d_tagtyp = mktag($2, ENUM, 1, 0);
+ } enum_declaration {
+ $$ = compltag(dcs->d_tagtyp, $4);
+ }
+ | enum {
+ dcs->d_tagtyp = mktag(NULL, ENUM, 1, 0);
+ } enum_declaration {
+ $$ = compltag(dcs->d_tagtyp, $3);
+ }
+ | enum error {
+ symtyp = FVFT;
+ $$ = gettyp(INT);
+ }
+ ;
+
+enum:
+ T_ENUM {
+ symtyp = FTAG;
+ pushdecl(ENUMCON);
+ }
+ ;
+
+enum_tag:
+ identifier {
+ $$ = getsym($1);
+ }
+ ;
+
+enum_declaration:
+ enum_decl_lbrace enums_with_opt_comma T_RBRACE {
+ $$ = $2;
+ }
+ ;
+
+enum_decl_lbrace:
+ T_LBRACE {
+ symtyp = FVFT;
+ enumval = 0;
+ }
+ ;
+
+enums_with_opt_comma:
+ enums {
+ $$ = $1;
+ }
+ | enums T_COMMA {
+ if (sflag) {
+ /* trailing "," prohibited in enum declaration */
+ error(54);
+ } else {
+ /* trailing "," prohibited in enum declaration */
+ warning(54);
+ }
+ $$ = $1;
+ }
+ ;
+
+enums:
+ enumerator {
+ $$ = $1;
+ }
+ | enums T_COMMA enumerator {
+ $$ = lnklst($1, $3);
+ }
+ | error {
+ $$ = NULL;
+ }
+ ;
+
+enumerator:
+ ename {
+ $$ = ename($1, enumval, 1);
+ }
+ | ename T_ASSIGN constant {
+ $$ = ename($1, toicon($3), 0);
+ }
+ ;
+
+ename:
+ identifier {
+ $$ = getsym($1);
+ }
+ ;
+
+
+notype_init_decls:
+ notype_init_decl
+ | notype_init_decls T_COMMA type_init_decl
+ ;
+
+type_init_decls:
+ type_init_decl
+ | type_init_decls T_COMMA type_init_decl
+ ;
+
+notype_init_decl:
+ notype_decl opt_asm_spec {
+ idecl($1, 0);
+ chksz($1);
+ }
+ | notype_decl opt_asm_spec {
+ idecl($1, 1);
+ } T_ASSIGN initializer {
+ chksz($1);
+ }
+ ;
+
+type_init_decl:
+ type_decl opt_asm_spec {
+ idecl($1, 0);
+ chksz($1);
+ }
+ | type_decl opt_asm_spec {
+ idecl($1, 1);
+ } T_ASSIGN initializer {
+ chksz($1);
+ }
+ ;
+
+notype_decl:
+ notype_direct_decl {
+ $$ = $1;
+ }
+ | pointer notype_direct_decl {
+ $$ = addptr($2, $1);
+ }
+ ;
+
+notype_direct_decl:
+ T_NAME {
+ $$ = dname(getsym($1));
+ }
+ | T_LPARN type_decl T_RPARN {
+ $$ = $2;
+ }
+ | notype_direct_decl T_LBRACK T_RBRACK {
+ $$ = addarray($1, 0, 0);
+ }
+ | notype_direct_decl T_LBRACK constant T_RBRACK {
+ $$ = addarray($1, 1, toicon($3));
+ }
+ | notype_direct_decl param_list {
+ $$ = addfunc($1, $2);
+ popdecl();
+ blklev--;
+ }
+ ;
+
+type_decl:
+ type_direct_decl {
+ $$ = $1;
+ }
+ | pointer type_direct_decl {
+ $$ = addptr($2, $1);
+ }
+ ;
+
+type_direct_decl:
+ identifier {
+ $$ = dname(getsym($1));
+ }
+ | T_LPARN type_decl T_RPARN {
+ $$ = $2;
+ }
+ | type_direct_decl T_LBRACK T_RBRACK {
+ $$ = addarray($1, 0, 0);
+ }
+ | type_direct_decl T_LBRACK constant T_RBRACK {
+ $$ = addarray($1, 1, toicon($3));
+ }
+ | type_direct_decl param_list {
+ $$ = addfunc($1, $2);
+ popdecl();
+ blklev--;
+ }
+ ;
+
+/*
+ * param_decl and notype_param_decl exist to avoid a conflict in
+ * argument lists. A typename enclosed in parens should always be
+ * treated as a typename, not an argument.
+ * "typedef int a; f(int (a));" is "typedef int a; f(int foo(a));"
+ * not "typedef int a; f(int a);"
+ */
+param_decl:
+ direct_param_decl {
+ $$ = $1;
+ }
+ | pointer direct_param_decl {
+ $$ = addptr($2, $1);
+ }
+ ;
+
+direct_param_decl:
+ identifier {
+ $$ = dname(getsym($1));
+ }
+ | T_LPARN notype_param_decl T_RPARN {
+ $$ = $2;
+ }
+ | direct_param_decl T_LBRACK T_RBRACK {
+ $$ = addarray($1, 0, 0);
+ }
+ | direct_param_decl T_LBRACK constant T_RBRACK {
+ $$ = addarray($1, 1, toicon($3));
+ }
+ | direct_param_decl param_list {
+ $$ = addfunc($1, $2);
+ popdecl();
+ blklev--;
+ }
+ ;
+
+notype_param_decl:
+ direct_notype_param_decl {
+ $$ = $1;
+ }
+ | pointer direct_notype_param_decl {
+ $$ = addptr($2, $1);
+ }
+ ;
+
+direct_notype_param_decl:
+ T_NAME {
+ $$ = dname(getsym($1));
+ }
+ | T_LPARN notype_param_decl T_RPARN {
+ $$ = $2;
+ }
+ | direct_notype_param_decl T_LBRACK T_RBRACK {
+ $$ = addarray($1, 0, 0);
+ }
+ | direct_notype_param_decl T_LBRACK constant T_RBRACK {
+ $$ = addarray($1, 1, toicon($3));
+ }
+ | direct_notype_param_decl param_list {
+ $$ = addfunc($1, $2);
+ popdecl();
+ blklev--;
+ }
+ ;
+
+pointer:
+ asterisk {
+ $$ = $1;
+ }
+ | asterisk type_qualifier_list {
+ $$ = mergepq($1, $2);
+ }
+ | asterisk pointer {
+ $$ = mergepq($1, $2);
+ }
+ | asterisk type_qualifier_list pointer {
+ $$ = mergepq(mergepq($1, $2), $3);
+ }
+ ;
+
+asterisk:
+ T_MULT {
+ $$ = xcalloc(1, sizeof (pqinf_t));
+ $$->p_pcnt = 1;
+ }
+ ;
+
+type_qualifier_list:
+ type_qualifier {
+ $$ = $1;
+ }
+ | type_qualifier_list type_qualifier {
+ $$ = mergepq($1, $2);
+ }
+ ;
+
+type_qualifier:
+ T_QUAL {
+ $$ = xcalloc(1, sizeof (pqinf_t));
+ if ($1 == CONST) {
+ $$->p_const = 1;
+ } else {
+ $$->p_volatile = 1;
+ }
+ }
+ ;
+
+param_list:
+ id_list_lparn identifier_list T_RPARN {
+ $$ = $2;
+ }
+ | abs_decl_param_list {
+ $$ = $1;
+ }
+ ;
+
+id_list_lparn:
+ T_LPARN {
+ blklev++;
+ pushdecl(PARG);
+ }
+ ;
+
+identifier_list:
+ T_NAME {
+ $$ = iname(getsym($1));
+ }
+ | identifier_list T_COMMA T_NAME {
+ $$ = lnklst($1, iname(getsym($3)));
+ }
+ | identifier_list error {
+ $$ = $1;
+ }
+ ;
+
+abs_decl_param_list:
+ abs_decl_lparn T_RPARN {
+ $$ = NULL;
+ }
+ | abs_decl_lparn vararg_parameter_type_list T_RPARN {
+ dcs->d_proto = 1;
+ $$ = $2;
+ }
+ | abs_decl_lparn error T_RPARN {
+ $$ = NULL;
+ }
+ ;
+
+abs_decl_lparn:
+ T_LPARN {
+ blklev++;
+ pushdecl(PARG);
+ }
+ ;
+
+vararg_parameter_type_list:
+ parameter_type_list {
+ $$ = $1;
+ }
+ | parameter_type_list T_COMMA T_ELLIPSE {
+ dcs->d_vararg = 1;
+ $$ = $1;
+ }
+ | T_ELLIPSE {
+ if (sflag) {
+ /* ANSI C requires formal parameter before "..." */
+ error(84);
+ } else if (!tflag) {
+ /* ANSI C requires formal parameter before "..." */
+ warning(84);
+ }
+ dcs->d_vararg = 1;
+ $$ = NULL;
+ }
+ ;
+
+parameter_type_list:
+ parameter_declaration opt_asm_spec {
+ $$ = $1;
+ }
+ | parameter_type_list T_COMMA parameter_declaration opt_asm_spec {
+ $$ = lnklst($1, $3);
+ }
+ ;
+
+parameter_declaration:
+ declmods deftyp {
+ $$ = decl1arg(aname(), 0);
+ }
+ | declspecs deftyp {
+ $$ = decl1arg(aname(), 0);
+ }
+ | declmods deftyp notype_param_decl {
+ $$ = decl1arg($3, 0);
+ }
+ /*
+ * param_decl is needed because of following conflict:
+ * "typedef int a; f(int (a));" could be parsed as
+ * "function with argument a of type int", or
+ * "function with an abstract argument of type function".
+ * This grammar realizes the second case.
+ */
+ | declspecs deftyp param_decl {
+ $$ = decl1arg($3, 0);
+ }
+ | declmods deftyp abs_decl {
+ $$ = decl1arg($3, 0);
+ }
+ | declspecs deftyp abs_decl {
+ $$ = decl1arg($3, 0);
+ }
+ ;
+
+opt_asm_spec:
+ /* empty */
+ | T_ASM T_LPARN T_STRING T_RPARN {
+ freeyyv(&$3, T_STRING);
+ }
+ ;
+
+initializer:
+ init_expr
+ ;
+
+init_expr:
+ expr %prec T_COMMA {
+ mkinit($1);
+ }
+ | init_lbrace init_expr_list init_rbrace
+ | init_lbrace init_expr_list T_COMMA init_rbrace
+ | error
+ ;
+
+init_expr_list:
+ init_expr %prec T_COMMA
+ | init_expr_list T_COMMA init_expr
+ ;
+
+init_lbrace:
+ T_LBRACE {
+ initlbr();
+ }
+ ;
+
+init_rbrace:
+ T_RBRACE {
+ initrbr();
+ }
+ ;
+
+type_name:
+ {
+ pushdecl(ABSTRACT);
+ } abstract_declaration {
+ popdecl();
+ $$ = $2->s_type;
+ }
+ ;
+
+abstract_declaration:
+ noclass_declmods deftyp {
+ $$ = decl1abs(aname());
+ }
+ | noclass_declspecs deftyp {
+ $$ = decl1abs(aname());
+ }
+ | noclass_declmods deftyp abs_decl {
+ $$ = decl1abs($3);
+ }
+ | noclass_declspecs deftyp abs_decl {
+ $$ = decl1abs($3);
+ }
+ ;
+
+abs_decl:
+ pointer {
+ $$ = addptr(aname(), $1);
+ }
+ | direct_abs_decl {
+ $$ = $1;
+ }
+ | pointer direct_abs_decl {
+ $$ = addptr($2, $1);
+ }
+ ;
+
+direct_abs_decl:
+ T_LPARN abs_decl T_RPARN {
+ $$ = $2;
+ }
+ | T_LBRACK T_RBRACK {
+ $$ = addarray(aname(), 0, 0);
+ }
+ | T_LBRACK constant T_RBRACK {
+ $$ = addarray(aname(), 1, toicon($2));
+ }
+ | direct_abs_decl T_LBRACK T_RBRACK {
+ $$ = addarray($1, 0, 0);
+ }
+ | direct_abs_decl T_LBRACK constant T_RBRACK {
+ $$ = addarray($1, 1, toicon($3));
+ }
+ | abs_decl_param_list {
+ $$ = addfunc(aname(), $1);
+ popdecl();
+ blklev--;
+ }
+ | direct_abs_decl abs_decl_param_list {
+ $$ = addfunc($1, $2);
+ popdecl();
+ blklev--;
+ }
+ ;
+
+stmnt:
+ labeled_stmnt
+ | expr_stmnt
+ | comp_stmnt
+ | selection_stmnt
+ | iteration_stmnt
+ | jump_stmnt {
+ ftflg = 0;
+ }
+ | asm_stmnt
+ ;
+
+labeled_stmnt:
+ label stmnt
+ ;
+
+label:
+ identifier T_COLON {
+ symtyp = FLAB;
+ label(T_NAME, getsym($1), NULL);
+ }
+ | T_CASE constant T_COLON {
+ label(T_CASE, NULL, $2);
+ ftflg = 1;
+ }
+ | T_DEFAULT T_COLON {
+ label(T_DEFAULT, NULL, NULL);
+ ftflg = 1;
+ }
+ ;
+
+comp_stmnt:
+ compstmnt_lbrace declaration_list opt_stmnt_list compstmnt_rbrace
+ | compstmnt_lbrace opt_stmnt_list compstmnt_rbrace
+ ;
+
+compstmnt_lbrace:
+ T_LBRACE {
+ blklev++;
+ mblklev++;
+ pushdecl(AUTO);
+ }
+ ;
+
+compstmnt_rbrace:
+ T_RBRACE {
+ popdecl();
+ freeblk();
+ mblklev--;
+ blklev--;
+ ftflg = 0;
+ }
+ ;
+
+opt_stmnt_list:
+ /* empty */
+ | stmnt_list
+ ;
+
+stmnt_list:
+ stmnt {
+ clrwflgs();
+ }
+ | stmnt_list stmnt {
+ clrwflgs();
+ }
+ | stmnt_list error T_SEMI {
+ clrwflgs();
+ }
+ ;
+
+expr_stmnt:
+ expr T_SEMI {
+ expr($1, 0, 0);
+ ftflg = 0;
+ }
+ | T_SEMI {
+ ftflg = 0;
+ }
+ ;
+
+selection_stmnt:
+ if_without_else {
+ if2();
+ if3(0);
+ }
+ | if_without_else T_ELSE {
+ if2();
+ } stmnt {
+ if3(1);
+ }
+ | if_without_else T_ELSE error {
+ if3(0);
+ }
+ | switch_expr stmnt {
+ switch2();
+ }
+ | switch_expr error {
+ switch2();
+ }
+ ;
+
+if_without_else:
+ if_expr stmnt
+ | if_expr error
+ ;
+
+if_expr:
+ T_IF T_LPARN expr T_RPARN {
+ if1($3);
+ clrwflgs();
+ }
+ ;
+
+switch_expr:
+ T_SWITCH T_LPARN expr T_RPARN {
+ switch1($3);
+ clrwflgs();
+ }
+ ;
+
+iteration_stmnt:
+ while_expr stmnt {
+ while2();
+ }
+ | while_expr error {
+ while2();
+ }
+ | do stmnt do_while_expr {
+ do2($3);
+ ftflg = 0;
+ }
+ | do error {
+ do2(NULL);
+ }
+ | for_exprs stmnt {
+ for2();
+ }
+ | for_exprs error {
+ for2();
+ }
+ ;
+
+while_expr:
+ T_WHILE T_LPARN expr T_RPARN {
+ while1($3);
+ clrwflgs();
+ }
+ ;
+
+do:
+ T_DO {
+ do1();
+ }
+ ;
+
+do_while_expr:
+ T_WHILE T_LPARN expr T_RPARN T_SEMI {
+ $$ = $3;
+ }
+ ;
+
+for_exprs:
+ T_FOR T_LPARN opt_expr T_SEMI opt_expr T_SEMI opt_expr T_RPARN {
+ for1($3, $5, $7);
+ clrwflgs();
+ }
+ ;
+
+opt_expr:
+ /* empty */ {
+ $$ = NULL;
+ }
+ | expr {
+ $$ = $1;
+ }
+ ;
+
+jump_stmnt:
+ goto identifier T_SEMI {
+ dogoto(getsym($2));
+ }
+ | goto error T_SEMI {
+ symtyp = FVFT;
+ }
+ | T_CONTINUE T_SEMI {
+ docont();
+ }
+ | T_BREAK T_SEMI {
+ dobreak();
+ }
+ | T_RETURN T_SEMI {
+ doreturn(NULL);
+ }
+ | T_RETURN expr T_SEMI {
+ doreturn($2);
+ }
+ ;
+
+goto:
+ T_GOTO {
+ symtyp = FLAB;
+ }
+ ;
+
+asm_stmnt:
+ T_ASM T_LPARN read_until_rparn T_SEMI {
+ setasm();
+ }
+ | T_ASM T_QUAL T_LPARN read_until_rparn T_SEMI {
+ setasm();
+ }
+ | T_ASM error
+ ;
+
+read_until_rparn:
+ /* empty */ {
+ ignuptorp();
+ }
+ ;
+
+declaration_list:
+ declaration {
+ clrwflgs();
+ }
+ | declaration_list declaration {
+ clrwflgs();
+ }
+ ;
+
+constant:
+ expr %prec T_COMMA {
+ $$ = $1;
+ }
+ ;
+
+expr:
+ expr T_MULT expr {
+ $$ = build(MULT, $1, $3);
+ }
+ | expr T_DIVOP expr {
+ $$ = build($2, $1, $3);
+ }
+ | expr T_ADDOP expr {
+ $$ = build($2, $1, $3);
+ }
+ | expr T_SHFTOP expr {
+ $$ = build($2, $1, $3);
+ }
+ | expr T_RELOP expr {
+ $$ = build($2, $1, $3);
+ }
+ | expr T_EQOP expr {
+ $$ = build($2, $1, $3);
+ }
+ | expr T_AND expr {
+ $$ = build(AND, $1, $3);
+ }
+ | expr T_XOR expr {
+ $$ = build(XOR, $1, $3);
+ }
+ | expr T_OR expr {
+ $$ = build(OR, $1, $3);
+ }
+ | expr T_LOGAND expr {
+ $$ = build(LOGAND, $1, $3);
+ }
+ | expr T_LOGOR expr {
+ $$ = build(LOGOR, $1, $3);
+ }
+ | expr T_QUEST expr T_COLON expr {
+ $$ = build(QUEST, $1, build(COLON, $3, $5));
+ }
+ | expr T_ASSIGN expr {
+ $$ = build(ASSIGN, $1, $3);
+ }
+ | expr T_OPASS expr {
+ $$ = build($2, $1, $3);
+ }
+ | expr T_COMMA expr {
+ $$ = build(COMMA, $1, $3);
+ }
+ | term {
+ $$ = $1;
+ }
+ ;
+
+term:
+ T_NAME {
+ /* XXX realy neccessary? */
+ if (yychar < 0)
+ yychar = yylex();
+ $$ = getnnode(getsym($1), yychar);
+ }
+ | string {
+ $$ = getsnode($1);
+ }
+ | T_CON {
+ $$ = getcnode(gettyp($1->v_tspec), $1);
+ }
+ | T_LPARN expr T_RPARN {
+ if ($2 != NULL)
+ $2->tn_parn = 1;
+ $$ = $2;
+ }
+ | term T_INCDEC {
+ $$ = build($2 == INC ? INCAFT : DECAFT, $1, NULL);
+ }
+ | T_INCDEC term {
+ $$ = build($1 == INC ? INCBEF : DECBEF, $2, NULL);
+ }
+ | T_MULT term {
+ $$ = build(STAR, $2, NULL);
+ }
+ | T_AND term {
+ $$ = build(AMPER, $2, NULL);
+ }
+ | T_UNOP term {
+ $$ = build($1, $2, NULL);
+ }
+ | T_ADDOP term {
+ if (tflag && $1 == PLUS) {
+ /* unary + is illegal in traditional C */
+ warning(100);
+ }
+ $$ = build($1 == PLUS ? UPLUS : UMINUS, $2, NULL);
+ }
+ | term T_LBRACK expr T_RBRACK {
+ $$ = build(STAR, build(PLUS, $1, $3), NULL);
+ }
+ | term T_LPARN T_RPARN {
+ $$ = funccall($1, NULL);
+ }
+ | term T_LPARN func_arg_list T_RPARN {
+ $$ = funccall($1, $3);
+ }
+ | term point_or_arrow T_NAME {
+ if ($1 != NULL) {
+ sym_t *msym;
+ /* XXX strmemb should be integrated in build() */
+ if ($2 == ARROW) {
+ /* must to this before strmemb is called */
+ $1 = cconv($1);
+ }
+ msym = strmemb($1, $2, getsym($3));
+ $$ = build($2, $1, getnnode(msym, 0));
+ } else {
+ $$ = NULL;
+ }
+ }
+ | T_SIZEOF term %prec T_SIZEOF {
+ if (($$ = $2 == NULL ? NULL : bldszof($2->tn_type)) != NULL)
+ chkmisc($2, 0, 0, 0, 0, 0, 1);
+ }
+ | T_SIZEOF T_LPARN type_name T_RPARN %prec T_SIZEOF {
+ $$ = bldszof($3);
+ }
+ | T_LPARN type_name T_RPARN term %prec T_UNOP {
+ $$ = cast($4, $2);
+ }
+ ;
+
+string:
+ T_STRING {
+ $$ = $1;
+ }
+ | T_STRING string2 {
+ $$ = catstrg($1, $2);
+ }
+ ;
+
+string2:
+ T_STRING {
+ if (tflag) {
+ /* concatenated strings are illegal in traditional C */
+ warning(219);
+ }
+ $$ = $1;
+ }
+ | string2 T_STRING {
+ $$ = catstrg($1, $2);
+ }
+ ;
+
+func_arg_list:
+ expr %prec T_COMMA {
+ $$ = funcarg(NULL, $1);
+ }
+ | func_arg_list T_COMMA expr {
+ $$ = funcarg($1, $3);
+ }
+ ;
+
+point_or_arrow:
+ T_STROP {
+ symtyp = FMOS;
+ $$ = $1;
+ }
+ ;
+
+identifier:
+ T_NAME {
+ $$ = $1;
+ }
+ | T_TYPENAME {
+ $$ = $1;
+ }
+ ;
+
+%%
+
+/* ARGSUSED */
+int
+yyerror(msg)
+ char *msg;
+{
+ error(249);
+ if (++sytxerr >= 5)
+ norecover();
+ return (0);
+}
+
+/*
+ * Gets a node for a constant and returns the value of this constant
+ * as integer.
+ * Is the node not constant or too large for int or of type float,
+ * a warning will be printed.
+ *
+ * toicon() should be used only inside declarations. If it is used in
+ * expressions, it frees the memory used for the expression.
+ */
+static int
+toicon(tn)
+ tnode_t *tn;
+{
+ int i;
+ tspec_t t;
+ val_t *v;
+
+ v = constant(tn);
+
+ /*
+ * Abstract declarations are used inside expression. To free
+ * the memory would be a fatal error.
+ */
+ if (dcs->d_ctx != ABSTRACT)
+ tfreeblk();
+
+ if ((t = v->v_tspec) == FLOAT || t == DOUBLE || t == LDOUBLE) {
+ i = (int)v->v_ldbl;
+ /* integral constant expression expected */
+ error(55);
+ } else {
+ i = (int)v->v_quad;
+ if (isutyp(t)) {
+ if ((u_quad_t)v->v_quad > INT_MAX) {
+ /* integral constant too large */
+ warning(56);
+ }
+ } else {
+#ifdef XXX_BROKEN_GCC
+ if (v->v_quad > INT_MAX) {
+ /* integral constant too large */
+ warning(56);
+ }
+ else if (v->v_quad < INT_MIN) {
+ /* integral constant too large */
+ warning(56);
+ }
+#else
+ if (v->v_quad > INT_MAX || v->v_quad < INT_MIN) {
+ /* integral constant too large */
+ warning(56);
+ }
+#endif
+ }
+ }
+ free(v);
+ return (i);
+}
+
+static void
+idecl(decl, initflg)
+ sym_t *decl;
+ int initflg;
+{
+ initerr = 0;
+ initsym = decl;
+
+ switch (dcs->d_ctx) {
+ case EXTERN:
+ decl1ext(decl, initflg);
+ break;
+ case ARG:
+ (void)decl1arg(decl, initflg);
+ break;
+ case AUTO:
+ decl1loc(decl, initflg);
+ break;
+ default:
+ lerror("idecl()");
+ }
+
+ if (initflg && !initerr)
+ prepinit();
+}
+
+/*
+ * Discard all input tokens up to and including the next
+ * unmatched right paren
+ */
+void
+ignuptorp()
+{
+ int level;
+
+ if (yychar < 0)
+ yychar = yylex();
+ freeyyv(&yylval, yychar);
+
+ level = 1;
+ while (yychar != T_RPARN || --level > 0) {
+ if (yychar == T_LPARN) {
+ level++;
+ } else if (yychar <= 0) {
+ break;
+ }
+ freeyyv(&yylval, yychar = yylex());
+ }
+
+ yyclearin;
+}
diff --git a/usr.bin/xlint/lint1/decl.c b/usr.bin/xlint/lint1/decl.c
new file mode 100644
index 0000000..1151166
--- /dev/null
+++ b/usr.bin/xlint/lint1/decl.c
@@ -0,0 +1,3135 @@
+/* $NetBSD: decl.c,v 1.11 1995/10/02 17:34:16 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: decl.c,v 1.11 1995/10/02 17:34:16 jpo Exp $";
+#endif
+
+#include <sys/param.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lint1.h"
+
+const char *unnamed = "<unnamed>";
+
+/* contains various information and classification on types */
+ttab_t ttab[NTSPEC];
+
+/* shared type structures for arithmtic types and void */
+static type_t *typetab;
+
+/* value of next enumerator during declaration of enum types */
+int enumval;
+
+/*
+ * pointer to top element of a stack which contains informations local
+ * to nested declarations
+ */
+dinfo_t *dcs;
+
+static type_t *tdeferr __P((type_t *, tspec_t));
+static void settdsym __P((type_t *, sym_t *));
+static tspec_t mrgtspec __P((tspec_t, tspec_t));
+static void align __P((int, int));
+static sym_t *newtag __P((sym_t *, scl_t, int, int));
+static int eqargs __P((type_t *, type_t *, int *));
+static int mnoarg __P((type_t *, int *));
+static int chkosdef __P((sym_t *, sym_t *));
+static int chkptdecl __P((sym_t *, sym_t *));
+static sym_t *nsfunc __P((sym_t *, sym_t *));
+static void osfunc __P((sym_t *, sym_t *));
+static void ledecl __P((sym_t *));
+static int chkinit __P((sym_t *));
+static void chkausg __P((int, sym_t *));
+static void chkvusg __P((int, sym_t *));
+static void chklusg __P((sym_t *));
+static void chktusg __P((sym_t *));
+static void chkglvar __P((sym_t *));
+static void glchksz __P((sym_t *));
+
+/*
+ * initializes all global vars used in declarations
+ */
+void
+initdecl()
+{
+ int i;
+ static struct {
+ tspec_t it_tspec;
+ ttab_t it_ttab;
+ } ittab[] = {
+ { SIGNED, { 0, 0,
+ SIGNED, UNSIGN,
+ 0, 0, 0, 0, 0, "signed" } },
+ { UNSIGN, { 0, 0,
+ SIGNED, UNSIGN,
+ 0, 0, 0, 0, 0, "unsigned" } },
+ { CHAR, { CHAR_BIT, CHAR_BIT,
+ SCHAR, UCHAR,
+ 1, 0, 0, 1, 1, "char" } },
+ { SCHAR, { CHAR_BIT, CHAR_BIT,
+ SCHAR, UCHAR,
+ 1, 0, 0, 1, 1, "signed char" } },
+ { UCHAR, { CHAR_BIT, CHAR_BIT,
+ SCHAR, UCHAR,
+ 1, 1, 0, 1, 1, "unsigned char" } },
+ { SHORT, { sizeof (short) * CHAR_BIT, 2 * CHAR_BIT,
+ SHORT, USHORT,
+ 1, 0, 0, 1, 1, "short" } },
+ { USHORT, { sizeof (u_short) * CHAR_BIT, 2 * CHAR_BIT,
+ SHORT, USHORT,
+ 1, 1, 0, 1, 1, "unsigned short" } },
+ { INT, { sizeof (int) * CHAR_BIT, 3 * CHAR_BIT,
+ INT, UINT,
+ 1, 0, 0, 1, 1, "int" } },
+ { UINT, { sizeof (u_int) * CHAR_BIT, 3 * CHAR_BIT,
+ INT, UINT,
+ 1, 1, 0, 1, 1, "unsigned int" } },
+ { LONG, { sizeof (long) * CHAR_BIT, 4 * CHAR_BIT,
+ LONG, ULONG,
+ 1, 0, 0, 1, 1, "long" } },
+ { ULONG, { sizeof (u_long) * CHAR_BIT, 4 * CHAR_BIT,
+ LONG, ULONG,
+ 1, 1, 0, 1, 1, "unsigned long" } },
+ { QUAD, { sizeof (quad_t) * CHAR_BIT, 8 * CHAR_BIT,
+ QUAD, UQUAD,
+ 1, 0, 0, 1, 1, "long long" } },
+ { UQUAD, { sizeof (u_quad_t) * CHAR_BIT, 8 * CHAR_BIT,
+ QUAD, UQUAD,
+ 1, 1, 0, 1, 1, "unsigned long long" } },
+ { FLOAT, { sizeof (float) * CHAR_BIT, 4 * CHAR_BIT,
+ FLOAT, FLOAT,
+ 0, 0, 1, 1, 1, "float" } },
+ { DOUBLE, { sizeof (double) * CHAR_BIT, 8 * CHAR_BIT,
+ DOUBLE, DOUBLE,
+ 0, 0, 1, 1, 1, "double" } },
+ { LDOUBLE, { sizeof (ldbl_t) * CHAR_BIT, 10 * CHAR_BIT,
+ LDOUBLE, LDOUBLE,
+ 0, 0, 1, 1, 1, "long double" } },
+ { VOID, { -1, -1,
+ VOID, VOID,
+ 0, 0, 0, 0, 0, "void" } },
+ { STRUCT, { -1, -1,
+ STRUCT, STRUCT,
+ 0, 0, 0, 0, 0, "struct" } },
+ { UNION, { -1, -1,
+ UNION, UNION,
+ 0, 0, 0, 0, 0, "union" } },
+ { ENUM, { sizeof (int) * CHAR_BIT, 3 * CHAR_BIT,
+ ENUM, ENUM,
+ 1, 0, 0, 1, 1, "enum" } },
+ { PTR, { sizeof (void *) * CHAR_BIT, 4 * CHAR_BIT,
+ PTR, PTR,
+ 0, 1, 0, 0, 1, "pointer" } },
+ { ARRAY, { -1, -1,
+ ARRAY, ARRAY,
+ 0, 0, 0, 0, 0, "array" } },
+ { FUNC, { -1, -1,
+ FUNC, FUNC,
+ 0, 0, 0, 0, 0, "function" } },
+ };
+
+ /* declaration stack */
+ dcs = xcalloc(1, sizeof (dinfo_t));
+ dcs->d_ctx = EXTERN;
+ dcs->d_ldlsym = &dcs->d_dlsyms;
+
+ /* type information and classification */
+ for (i = 0; i < sizeof (ittab) / sizeof (ittab[0]); i++)
+ STRUCT_ASSIGN(ttab[ittab[i].it_tspec], ittab[i].it_ttab);
+ if (!pflag) {
+ for (i = 0; i < NTSPEC; i++)
+ ttab[i].tt_psz = ttab[i].tt_sz;
+ }
+
+ /* shared type structures */
+ typetab = xcalloc(NTSPEC, sizeof (type_t));
+ for (i = 0; i < NTSPEC; i++)
+ typetab[i].t_tspec = NOTSPEC;
+ typetab[CHAR].t_tspec = CHAR;
+ typetab[SCHAR].t_tspec = SCHAR;
+ typetab[UCHAR].t_tspec = UCHAR;
+ typetab[SHORT].t_tspec = SHORT;
+ typetab[USHORT].t_tspec = USHORT;
+ typetab[INT].t_tspec = INT;
+ typetab[UINT].t_tspec = UINT;
+ typetab[LONG].t_tspec = LONG;
+ typetab[ULONG].t_tspec = ULONG;
+ typetab[QUAD].t_tspec = QUAD;
+ typetab[UQUAD].t_tspec = UQUAD;
+ typetab[FLOAT].t_tspec = FLOAT;
+ typetab[DOUBLE].t_tspec = DOUBLE;
+ typetab[LDOUBLE].t_tspec = LDOUBLE;
+ typetab[VOID].t_tspec = VOID;
+ /*
+ * Next two are not real types. They are only used by the parser
+ * to return keywords "signed" and "unsigned"
+ */
+ typetab[SIGNED].t_tspec = SIGNED;
+ typetab[UNSIGN].t_tspec = UNSIGN;
+}
+
+/*
+ * Returns a shared type structure vor arithmetic types and void.
+ *
+ * It's important do duplicate this structure (using duptyp() or tdupdyp())
+ * if it is to be modified (adding qualifiers or anything else).
+ */
+type_t *
+gettyp(t)
+ tspec_t t;
+{
+ return (&typetab[t]);
+}
+
+type_t *
+duptyp(tp)
+ const type_t *tp;
+{
+ type_t *ntp;
+
+ ntp = getblk(sizeof (type_t));
+ STRUCT_ASSIGN(*ntp, *tp);
+ return (ntp);
+}
+
+/*
+ * Use tduptyp() instead of duptyp() inside expressions (if the
+ * allocated memory should be freed after the expr).
+ */
+type_t *
+tduptyp(tp)
+ const type_t *tp;
+{
+ type_t *ntp;
+
+ ntp = tgetblk(sizeof (type_t));
+ STRUCT_ASSIGN(*ntp, *tp);
+ return (ntp);
+}
+
+/*
+ * Returns 1 if the argument is void or an incomplete array,
+ * struct, union or enum type.
+ */
+int
+incompl(tp)
+ type_t *tp;
+{
+ tspec_t t;
+
+ if ((t = tp->t_tspec) == VOID) {
+ return (1);
+ } else if (t == ARRAY) {
+ return (tp->t_aincompl);
+ } else if (t == STRUCT || t == UNION) {
+ return (tp->t_str->sincompl);
+ } else if (t == ENUM) {
+ return (tp->t_enum->eincompl);
+ }
+ return (0);
+}
+
+/*
+ * Set the flag for (in)complete array, struct, union or enum
+ * types.
+ */
+void
+setcompl(tp, ic)
+ type_t *tp;
+ int ic;
+{
+ tspec_t t;
+
+ if ((t = tp->t_tspec) == ARRAY) {
+ tp->t_aincompl = ic;
+ } else if (t == STRUCT || t == UNION) {
+ tp->t_str->sincompl = ic;
+ } else {
+ if (t != ENUM)
+ lerror("setcompl() 1");
+ tp->t_enum->eincompl = ic;
+ }
+}
+
+/*
+ * Remember the storage class of the current declaration in dcs->d_scl
+ * (the top element of the declaration stack) and detect multiple
+ * storage classes.
+ */
+void
+addscl(sc)
+ scl_t sc;
+{
+ if (sc == INLINE) {
+ if (dcs->d_inline)
+ /* duplicate '%s' */
+ warning(10, "inline");
+ dcs->d_inline = 1;
+ return;
+ }
+ if (dcs->d_type != NULL || dcs->d_atyp != NOTSPEC ||
+ dcs->d_smod != NOTSPEC || dcs->d_lmod != NOTSPEC) {
+ /* storage class after type is obsolescent */
+ warning(83);
+ }
+ if (dcs->d_scl == NOSCL) {
+ dcs->d_scl = sc;
+ } else {
+ /*
+ * multiple storage classes. An error will be reported in
+ * deftyp().
+ */
+ dcs->d_mscl = 1;
+ }
+}
+
+/*
+ * Remember the type, modifier or typedef name returned by the parser
+ * in *dcs (top element of decl stack). This information is used in
+ * deftyp() to build the type used for all declarators in this
+ * declaration.
+ *
+ * Is tp->t_typedef 1, the type comes from a previously defined typename.
+ * Otherwise it comes from a type specifier (int, long, ...) or a
+ * struct/union/enum tag.
+ */
+void
+addtype(tp)
+ type_t *tp;
+{
+ tspec_t t;
+
+ if (tp->t_typedef) {
+ if (dcs->d_type != NULL || dcs->d_atyp != NOTSPEC ||
+ dcs->d_lmod != NOTSPEC || dcs->d_smod != NOTSPEC) {
+ /*
+ * something like "typedef int a; int a b;"
+ * This should not happen with current grammar.
+ */
+ lerror("addtype()");
+ }
+ dcs->d_type = tp;
+ return;
+ }
+
+ t = tp->t_tspec;
+
+ if (t == STRUCT || t == UNION || t == ENUM) {
+ /*
+ * something like "int struct a ..."
+ * struct/union/enum with anything else is not allowed
+ */
+ if (dcs->d_type != NULL || dcs->d_atyp != NOTSPEC ||
+ dcs->d_lmod != NOTSPEC || dcs->d_smod != NOTSPEC) {
+ /*
+ * remember that an error must be reported in
+ * deftyp().
+ */
+ dcs->d_terr = 1;
+ dcs->d_atyp = dcs->d_lmod = dcs->d_smod = NOTSPEC;
+ }
+ dcs->d_type = tp;
+ return;
+ }
+
+ if (dcs->d_type != NULL && !dcs->d_type->t_typedef) {
+ /*
+ * something like "struct a int"
+ * struct/union/enum with anything else is not allowed
+ */
+ dcs->d_terr = 1;
+ return;
+ }
+
+ if (t == LONG && dcs->d_lmod == LONG) {
+ /* "long long" or "long ... long" */
+ t = QUAD;
+ dcs->d_lmod = NOTSPEC;
+ if (!quadflg)
+ /* %s C does not support 'long long' */
+ (void)gnuism(265, tflag ? "traditional" : "ANSI");
+ }
+
+ if (dcs->d_type != NULL && dcs->d_type->t_typedef) {
+ /* something like "typedef int a; a long ..." */
+ dcs->d_type = tdeferr(dcs->d_type, t);
+ return;
+ }
+
+ /* now it can be only a combination of arithmetic types and void */
+ if (t == SIGNED || t == UNSIGN) {
+ /* remeber specifiers "signed" and "unsigned" in dcs->d_smod */
+ if (dcs->d_smod != NOTSPEC)
+ /*
+ * more then one "signed" and/or "unsigned"; print
+ * an error in deftyp()
+ */
+ dcs->d_terr = 1;
+ dcs->d_smod = t;
+ } else if (t == SHORT || t == LONG || t == QUAD) {
+ /*
+ * remember specifiers "short", "long" and "long long" in
+ * dcs->d_lmod
+ */
+ if (dcs->d_lmod != NOTSPEC)
+ /* more than one, print error in deftyp() */
+ dcs->d_terr = 1;
+ dcs->d_lmod = t;
+ } else {
+ /*
+ * remember specifiers "void", "char", "int", "float" or
+ * "double" int dcs->d_atyp
+ */
+ if (dcs->d_atyp != NOTSPEC)
+ /* more than one, print error in deftyp() */
+ dcs->d_terr = 1;
+ dcs->d_atyp = t;
+ }
+}
+
+/*
+ * called if a list of declaration specifiers contains a typedef name
+ * and other specifiers (except struct, union, enum, typedef name)
+ */
+static type_t *
+tdeferr(td, t)
+ type_t *td;
+ tspec_t t;
+{
+ tspec_t t2;
+
+ t2 = td->t_tspec;
+
+ switch (t) {
+ case SIGNED:
+ case UNSIGN:
+ if (t2 == CHAR || t2 == SHORT || t2 == INT || t2 == LONG ||
+ t2 == QUAD) {
+ if (!tflag)
+ /* modifying typedef with ... */
+ warning(5, ttab[t].tt_name);
+ td = duptyp(gettyp(mrgtspec(t2, t)));
+ td->t_typedef = 1;
+ return (td);
+ }
+ break;
+ case SHORT:
+ if (t2 == INT || t2 == UINT) {
+ /* modifying typedef with ... */
+ warning(5, "short");
+ td = duptyp(gettyp(t2 == INT ? SHORT : USHORT));
+ td->t_typedef = 1;
+ return (td);
+ }
+ break;
+ case LONG:
+ if (t2 == INT || t2 == UINT || t2 == LONG || t2 == ULONG ||
+ t2 == FLOAT || t2 == DOUBLE) {
+ /* modifying typedef with ... */
+ warning(5, "long");
+ if (t2 == INT) {
+ td = gettyp(LONG);
+ } else if (t2 == UINT) {
+ td = gettyp(ULONG);
+ } else if (t2 == LONG) {
+ td = gettyp(QUAD);
+ } else if (t2 == ULONG) {
+ td = gettyp(UQUAD);
+ } else if (t2 == FLOAT) {
+ td = gettyp(DOUBLE);
+ } else if (t2 == DOUBLE) {
+ td = gettyp(LDOUBLE);
+ }
+ td = duptyp(td);
+ td->t_typedef = 1;
+ return (td);
+ }
+ break;
+ /* LINTED (enumeration values not handled in switch) */
+ default:
+ }
+
+ /* Anything other is not accepted. */
+
+ dcs->d_terr = 1;
+ return (td);
+}
+
+/*
+ * Remember the symbol of a typedef name (2nd arg) in a struct, union
+ * or enum tag if the typedef name is the first defined for this tag.
+ *
+ * If the tag is unnamed, the typdef name is used for identification
+ * of this tag in lint2. Although its possible that more then one typedef
+ * name is defined for one tag, the first name defined should be unique
+ * if the tag is unnamed.
+ */
+static void
+settdsym(tp, sym)
+ type_t *tp;
+ sym_t *sym;
+{
+ tspec_t t;
+
+ if ((t = tp->t_tspec) == STRUCT || t == UNION) {
+ if (tp->t_str->stdef == NULL)
+ tp->t_str->stdef = sym;
+ } else if (t == ENUM) {
+ if (tp->t_enum->etdef == NULL)
+ tp->t_enum->etdef = sym;
+ }
+}
+
+/*
+ * Remember a qualifier which is part of the declaration specifiers
+ * (and not the declarator) in the top element of the declaration stack.
+ * Also detect multiple qualifiers of the same kind.
+
+ * The rememberd qualifier is used by deftyp() to construct the type
+ * for all declarators.
+ */
+void
+addqual(q)
+ tqual_t q;
+{
+ if (q == CONST) {
+ if (dcs->d_const) {
+ /* duplicate "%s" */
+ warning(10, "const");
+ }
+ dcs->d_const = 1;
+ } else {
+ if (q != VOLATILE)
+ lerror("addqual() 1");
+ if (dcs->d_volatile) {
+ /* duplicate "%s" */
+ warning(10, "volatile");
+ }
+ dcs->d_volatile = 1;
+ }
+}
+
+/*
+ * Go to the next declaration level (structs, nested structs, blocks,
+ * argument declaration lists ...)
+ */
+void
+pushdecl(sc)
+ scl_t sc;
+{
+ dinfo_t *di;
+
+ if (dflag)
+ (void)printf("pushdecl(%d)\n", (int)sc);
+
+ /* put a new element on the declaration stack */
+ di = xcalloc(1, sizeof (dinfo_t));
+ di->d_nxt = dcs;
+ dcs = di;
+ di->d_ctx = sc;
+ di->d_ldlsym = &di->d_dlsyms;
+}
+
+/*
+ * Go back to previous declaration level
+ */
+void
+popdecl()
+{
+ dinfo_t *di;
+
+ if (dflag)
+ (void)printf("popdecl(%d)\n", (int)dcs->d_ctx);
+
+ if (dcs->d_nxt == NULL)
+ lerror("popdecl() 1");
+ di = dcs;
+ dcs = di->d_nxt;
+ switch (di->d_ctx) {
+ case EXTERN:
+ /* there is nothing after external declarations */
+ lerror("popdecl() 2");
+ /* NOTREACHED */
+ case MOS:
+ case MOU:
+ case ENUMCON:
+ /*
+ * Symbols declared in (nested) structs or enums are
+ * part of the next level (they are removed from the
+ * symbol table if the symbols of the outher level are
+ * removed)
+ */
+ if ((*dcs->d_ldlsym = di->d_dlsyms) != NULL)
+ dcs->d_ldlsym = di->d_ldlsym;
+ break;
+ case ARG:
+ /*
+ * All symbols in dcs->d_dlsyms are introduced in old style
+ * argument declarations (it's not clean, but possible).
+ * They are appended to the list of symbols declared in
+ * an old style argument identifier list or a new style
+ * parameter type list.
+ */
+ if (di->d_dlsyms != NULL) {
+ *di->d_ldlsym = dcs->d_fpsyms;
+ dcs->d_fpsyms = di->d_dlsyms;
+ }
+ break;
+ case ABSTRACT:
+ /*
+ * casts and sizeof
+ * Append all symbols declared in the abstract declaration
+ * to the list of symbols declared in the surounding decl.
+ * or block.
+ * XXX I'm not sure whether they should be removed from the
+ * symbol table now or later.
+ */
+ if ((*dcs->d_ldlsym = di->d_dlsyms) != NULL)
+ dcs->d_ldlsym = di->d_ldlsym;
+ break;
+ case AUTO:
+ /* check usage of local vars */
+ chkusage(di);
+ /* FALLTHROUGH */
+ case PARG:
+ /* usage of arguments will be checked by funcend() */
+ rmsyms(di->d_dlsyms);
+ break;
+ default:
+ lerror("popdecl() 3");
+ }
+ free(di);
+}
+
+/*
+ * Set flag d_asm in all declaration stack elements up to the
+ * outermost one.
+ *
+ * This is used to mark compound statements which have, possibly in
+ * nested compound statements, asm statements. For these compound
+ * statements no warnings about unused or unitialized variables are
+ * printed.
+ *
+ * There is no need to clear d_asm in dinfo structs with context AUTO,
+ * because these structs are freed at the end of the compound statement.
+ * But it must be cleard in the outermost dinfo struct, which has
+ * context EXTERN. This could be done in clrtyp() and would work for
+ * C, but not for C++ (due to mixed statements and declarations). Thus
+ * we clear it in glclup(), which is used to do some cleanup after
+ * global declarations/definitions.
+ */
+void
+setasm()
+{
+ dinfo_t *di;
+
+ for (di = dcs; di != NULL; di = di->d_nxt)
+ di->d_asm = 1;
+}
+
+/*
+ * Clean all elements of the top element of declaration stack which
+ * will be used by the next declaration
+ */
+void
+clrtyp()
+{
+ dcs->d_atyp = dcs->d_smod = dcs->d_lmod = NOTSPEC;
+ dcs->d_scl = NOSCL;
+ dcs->d_type = NULL;
+ dcs->d_const = dcs->d_volatile = 0;
+ dcs->d_inline = 0;
+ dcs->d_mscl = dcs->d_terr = 0;
+ dcs->d_nedecl = 0;
+ dcs->d_notyp = 0;
+}
+
+/*
+ * Create a type structure from the informations gathered in
+ * the declaration stack.
+ * Complain about storage classes which are not possible in current
+ * context.
+ */
+void
+deftyp()
+{
+ tspec_t t, s, l;
+ type_t *tp;
+ scl_t scl;
+
+ t = dcs->d_atyp; /* CHAR, INT, FLOAT, DOUBLE, VOID */
+ s = dcs->d_smod; /* SIGNED, UNSIGNED */
+ l = dcs->d_lmod; /* SHORT, LONG, QUAD */
+ tp = dcs->d_type;
+ scl = dcs->d_scl;
+
+ if (t == NOTSPEC && s == NOTSPEC && l == NOTSPEC && tp == NULL)
+ dcs->d_notyp = 1;
+
+ if (tp != NULL && (t != NOTSPEC || s != NOTSPEC || l != NOTSPEC)) {
+ /* should never happen */
+ lerror("deftyp() 1");
+ }
+
+ if (tp == NULL) {
+ switch (t) {
+ case NOTSPEC:
+ t = INT;
+ /* FALLTHROUGH */
+ case INT:
+ if (s == NOTSPEC)
+ s = SIGNED;
+ break;
+ case CHAR:
+ if (l != NOTSPEC) {
+ dcs->d_terr = 1;
+ l = NOTSPEC;
+ }
+ break;
+ case FLOAT:
+ if (l == LONG) {
+ l = NOTSPEC;
+ t = DOUBLE;
+ if (!tflag)
+ /* use 'double' instead of ... */
+ warning(6);
+ }
+ break;
+ case DOUBLE:
+ if (l == LONG) {
+ l = NOTSPEC;
+ t = LDOUBLE;
+ if (tflag)
+ /* 'long double' is illegal in ... */
+ warning(266);
+ }
+ break;
+ case VOID:
+ break;
+ default:
+ lerror("deftyp() 2");
+ }
+ if (t != INT && t != CHAR && (s != NOTSPEC || l != NOTSPEC)) {
+ dcs->d_terr = 1;
+ l = s = NOTSPEC;
+ }
+ if (l != NOTSPEC)
+ t = l;
+ dcs->d_type = gettyp(mrgtspec(t, s));
+ }
+
+ if (dcs->d_mscl) {
+ /* only one storage class allowed */
+ error(7);
+ }
+ if (dcs->d_terr) {
+ /* illegal type combination */
+ error(4);
+ }
+
+ if (dcs->d_ctx == EXTERN) {
+ if (scl == REG || scl == AUTO) {
+ /* illegal storage class */
+ error(8);
+ scl = NOSCL;
+ }
+ } else if (dcs->d_ctx == ARG || dcs->d_ctx == PARG) {
+ if (scl != NOSCL && scl != REG) {
+ /* only "register" valid ... */
+ error(9);
+ scl = NOSCL;
+ }
+ }
+
+ dcs->d_scl = scl;
+
+ if (dcs->d_const && dcs->d_type->t_const) {
+ if (!dcs->d_type->t_typedef)
+ lerror("deftyp() 3");
+ /* typedef already qualified with "%s" */
+ warning(68, "const");
+ }
+ if (dcs->d_volatile && dcs->d_type->t_volatile) {
+ if (!dcs->d_type->t_typedef)
+ lerror("deftyp() 4");
+ /* typedef already qualified with "%s" */
+ warning(68, "volatile");
+ }
+
+ if (dcs->d_const || dcs->d_volatile) {
+ dcs->d_type = duptyp(dcs->d_type);
+ dcs->d_type->t_const |= dcs->d_const;
+ dcs->d_type->t_volatile |= dcs->d_volatile;
+ }
+}
+
+/*
+ * Merge type specifiers (char, ..., long long, signed, unsigned).
+ */
+static tspec_t
+mrgtspec(t, s)
+ tspec_t t, s;
+{
+ if (s == SIGNED || s == UNSIGN) {
+ if (t == CHAR) {
+ t = s == SIGNED ? SCHAR : UCHAR;
+ } else if (t == SHORT) {
+ t = s == SIGNED ? SHORT : USHORT;
+ } else if (t == INT) {
+ t = s == SIGNED ? INT : UINT;
+ } else if (t == LONG) {
+ t = s == SIGNED ? LONG : ULONG;
+ } else if (t == QUAD) {
+ t = s == SIGNED ? QUAD : UQUAD;
+ }
+ }
+
+ return (t);
+}
+
+/*
+ * Return the length of a type in bit.
+ *
+ * Printing a message if the outhermost dimension of an array is 0 must
+ * be done by the caller. All other problems are reported by length()
+ * if name is not NULL.
+ */
+int
+length(tp, name)
+ type_t *tp;
+ const char *name;
+{
+ int elem, elsz;
+
+ elem = 1;
+ while (tp->t_tspec == ARRAY) {
+ elem *= tp->t_dim;
+ tp = tp->t_subt;
+ }
+ switch (tp->t_tspec) {
+ case FUNC:
+ /* compiler takes size of function */
+ lerror(msgs[12]);
+ /* NOTREACHED */
+ case STRUCT:
+ case UNION:
+ if (incompl(tp) && name != NULL) {
+ /* incomplete structure or union %s: %s */
+ error(31, tp->t_str->stag->s_name, name);
+ }
+ elsz = tp->t_str->size;
+ break;
+ case ENUM:
+ if (incompl(tp) && name != NULL) {
+ /* incomplete enum type: %s */
+ warning(13, name);
+ }
+ /* FALLTHROUGH */
+ default:
+ elsz = size(tp->t_tspec);
+ if (elsz <= 0)
+ lerror("length()");
+ break;
+ }
+ return (elem * elsz);
+}
+
+/*
+ * Get the alignment of the given Type in bits.
+ */
+int
+getbound(tp)
+ type_t *tp;
+{
+ int a;
+ tspec_t t;
+
+ while (tp->t_tspec == ARRAY)
+ tp = tp->t_subt;
+
+ if ((t = tp->t_tspec) == STRUCT || t == UNION) {
+ a = tp->t_str->align;
+ } else if (t == FUNC) {
+ /* compiler takes alignment of function */
+ error(14);
+ a = ALIGN(1) * CHAR_BIT;
+ } else {
+ if ((a = size(t)) == 0) {
+ a = CHAR_BIT;
+ } else if (a > ALIGN(1) * CHAR_BIT) {
+ a = ALIGN(1) * CHAR_BIT;
+ }
+ }
+ if (a < CHAR_BIT || a > ALIGN(1) * CHAR_BIT)
+ lerror("getbound() 1");
+ return (a);
+}
+
+/*
+ * Concatenate two lists of symbols by s_nxt. Used by declarations of
+ * struct/union/enum elements and parameters.
+ */
+sym_t *
+lnklst(l1, l2)
+ sym_t *l1, *l2;
+{
+ sym_t *l;
+
+ if ((l = l1) == NULL)
+ return (l2);
+ while (l1->s_nxt != NULL)
+ l1 = l1->s_nxt;
+ l1->s_nxt = l2;
+ return (l);
+}
+
+/*
+ * Check if the type of the given symbol is valid and print an error
+ * message if it is not.
+ *
+ * Invalid types are:
+ * - arrays of incomlete types or functions
+ * - functions returning arrays or functions
+ * - void types other than type of function or pointer
+ */
+void
+chktyp(sym)
+ sym_t *sym;
+{
+ tspec_t to, t;
+ type_t **tpp, *tp;
+
+ tpp = &sym->s_type;
+ to = NOTSPEC;
+ while ((tp = *tpp) != NULL) {
+ t = tp->t_tspec;
+ /*
+ * If this is the type of an old style function definition,
+ * a better warning is printed in funcdef().
+ */
+ if (t == FUNC && !tp->t_proto &&
+ !(to == NOTSPEC && sym->s_osdef)) {
+ if (sflag && hflag)
+ /* function declaration is not a prototype */
+ warning(287);
+ }
+ if (to == FUNC) {
+ if (t == FUNC || t == ARRAY) {
+ /* function returns illegal type */
+ error(15);
+ if (t == FUNC) {
+ *tpp = incref(*tpp, PTR);
+ } else {
+ *tpp = incref((*tpp)->t_subt, PTR);
+ }
+ return;
+ } else if (tp->t_const || tp->t_volatile) {
+ if (sflag) { /* XXX oder better !tflag ? */
+ /* function cannot return const... */
+ warning(228);
+ }
+ }
+ } if (to == ARRAY) {
+ if (t == FUNC) {
+ /* array of function is illegal */
+ error(16);
+ *tpp = gettyp(INT);
+ return;
+ } else if (t == ARRAY && tp->t_dim == 0) {
+ /* null dimension */
+ error(17);
+ return;
+ } else if (t == VOID) {
+ /* illegal use of void */
+ error(18);
+ *tpp = gettyp(INT);
+#if 0 /* errors are produced by length() */
+ } else if (incompl(tp)) {
+ /* array of incomplete type */
+ if (sflag) {
+ error(301);
+ } else {
+ warning(301);
+ }
+#endif
+ }
+ } else if (to == NOTSPEC && t == VOID) {
+ if (dcs->d_ctx == PARG) {
+ if (sym->s_scl != ABSTRACT) {
+ if (sym->s_name == unnamed)
+ lerror("chktyp()");
+ /* void param cannot have name: %s */
+ error(61, sym->s_name);
+ *tpp = gettyp(INT);
+ }
+ } else if (dcs->d_ctx == ABSTRACT) {
+ /* ok */
+ } else if (sym->s_scl != TYPEDEF) {
+ /* void type for %s */
+ error(19, sym->s_name);
+ *tpp = gettyp(INT);
+ }
+ }
+ if (t == VOID && to != PTR) {
+ if (tp->t_const || tp->t_volatile) {
+ /* inappropriate qualifiers with "void" */
+ warning(69);
+ tp->t_const = tp->t_volatile = 0;
+ }
+ }
+ tpp = &tp->t_subt;
+ to = t;
+ }
+}
+
+/*
+ * Process the declarator of a struct/union element.
+ */
+sym_t *
+decl1str(dsym)
+ sym_t *dsym;
+{
+ type_t *tp;
+ tspec_t t;
+ int sz, o, len;
+ scl_t sc;
+
+ if ((sc = dsym->s_scl) != MOS && sc != MOU)
+ lerror("decl1str() 1");
+
+ if (dcs->d_rdcsym != NULL) {
+ if ((sc = dcs->d_rdcsym->s_scl) != MOS && sc != MOU)
+ /* should be ensured by storesym() */
+ lerror("decl1str() 2");
+ if (dsym->s_styp == dcs->d_rdcsym->s_styp) {
+ /* duplicate member name: %s */
+ error(33, dsym->s_name);
+ rmsym(dcs->d_rdcsym);
+ }
+ }
+
+ chktyp(dsym);
+
+ t = (tp = dsym->s_type)->t_tspec;
+
+ if (dsym->s_field) {
+ /*
+ * bit field
+ *
+ * only unsigned und signed int are protable bit-field types
+ *(at least in ANSI C, in traditional C only unsigned int)
+ */
+ if (t == CHAR || t == UCHAR || t == SCHAR ||
+ t == SHORT || t == USHORT || t == ENUM) {
+ if (sflag) {
+ /* bit-field type '%s' invalid in ANSI C */
+ warning(273, tyname(tp));
+ } else if (pflag) {
+ /* nonportable bit-field type */
+ warning(34);
+ }
+ } else if (t == INT && dcs->d_smod == NOTSPEC) {
+ if (pflag) {
+ /* nonportable bit-field type */
+ warning(34);
+ }
+ } else if (t != INT && t != UINT) {
+ /* illegal bit-field type */
+ error(35);
+ sz = tp->t_flen;
+ dsym->s_type = tp = duptyp(gettyp(t = INT));
+ if ((tp->t_flen = sz) > size(t))
+ tp->t_flen = size(t);
+ }
+ if ((len = tp->t_flen) < 0 || len > size(t)) {
+ /* illegal bit-field size */
+ error(36);
+ tp->t_flen = size(t);
+ } else if (len == 0 && dsym->s_name != unnamed) {
+ /* zero size bit-field */
+ error(37);
+ tp->t_flen = size(t);
+ }
+ if (dsym->s_scl == MOU) {
+ /* illegal use of bit-field */
+ error(41);
+ dsym->s_type->t_isfield = 0;
+ dsym->s_field = 0;
+ }
+ } else if (t == FUNC) {
+ /* function illegal in structure or union */
+ error(38);
+ dsym->s_type = tp = incref(tp, t = PTR);
+ }
+
+ /*
+ * bit-fields of length 0 are not warned about because length()
+ * does not return the length of the bit-field but the length
+ * of the type the bit-field is packed in (its ok)
+ */
+ if ((sz = length(dsym->s_type, dsym->s_name)) == 0) {
+ if (t == ARRAY && dsym->s_type->t_dim == 0) {
+ /* illegal zero sized structure member: %s */
+ warning(39, dsym->s_name);
+ }
+ }
+
+ if (dcs->d_ctx == MOU) {
+ o = dcs->d_offset;
+ dcs->d_offset = 0;
+ }
+ if (dsym->s_field) {
+ align(getbound(tp), tp->t_flen);
+ dsym->s_value.v_quad = (dcs->d_offset / size(t)) * size(t);
+ tp->t_foffs = dcs->d_offset - (int)dsym->s_value.v_quad;
+ dcs->d_offset += tp->t_flen;
+ } else {
+ align(getbound(tp), 0);
+ dsym->s_value.v_quad = dcs->d_offset;
+ dcs->d_offset += sz;
+ }
+ if (dcs->d_ctx == MOU) {
+ if (o > dcs->d_offset)
+ dcs->d_offset = o;
+ }
+
+ chkfdef(dsym, 0);
+
+ return (dsym);
+}
+
+/*
+ * Aligns next structure element as required.
+ *
+ * al contains the required alignment, len the length of a bit-field.
+ */
+static void
+align(al, len)
+ int al, len;
+{
+ int no;
+
+ /*
+ * The alignment of the current element becomes the alignment of
+ * the struct/union if it is larger than the current alignment
+ * of the struct/union.
+ */
+ if (al > dcs->d_stralign)
+ dcs->d_stralign = al;
+
+ no = (dcs->d_offset + (al - 1)) & ~(al - 1);
+ if (len == 0 || dcs->d_offset + len > no)
+ dcs->d_offset = no;
+}
+
+/*
+ * Remember the width of the field in its type structure.
+ */
+sym_t *
+bitfield(dsym, len)
+ sym_t *dsym;
+ int len;
+{
+ if (dsym == NULL) {
+ dsym = getblk(sizeof (sym_t));
+ dsym->s_name = unnamed;
+ dsym->s_kind = FMOS;
+ dsym->s_scl = MOS;
+ dsym->s_type = gettyp(INT);
+ dsym->s_blklev = -1;
+ }
+ dsym->s_type = duptyp(dsym->s_type);
+ dsym->s_type->t_isfield = 1;
+ dsym->s_type->t_flen = len;
+ dsym->s_field = 1;
+ return (dsym);
+}
+
+/*
+ * Collect informations about a sequence of asterisks and qualifiers
+ * in a list of type pqinf_t.
+ * Qualifiers refer always to the left asterisk. The rightmost asterisk
+ * will be at the top of the list.
+ */
+pqinf_t *
+mergepq(p1, p2)
+ pqinf_t *p1, *p2;
+{
+ pqinf_t *p;
+
+ if (p2->p_pcnt != 0) {
+ /* left '*' at the end of the list */
+ for (p = p2; p->p_nxt != NULL; p = p->p_nxt) ;
+ p->p_nxt = p1;
+ return (p2);
+ } else {
+ if (p2->p_const) {
+ if (p1->p_const) {
+ /* duplicate %s */
+ warning(10, "const");
+ }
+ p1->p_const = 1;
+ }
+ if (p2->p_volatile) {
+ if (p1->p_volatile) {
+ /* duplicate %s */
+ warning(10, "volatile");
+ }
+ p1->p_volatile = 1;
+ }
+ free(p2);
+ return (p1);
+ }
+}
+
+/*
+ * Followint 3 functions extend the type of a declarator with
+ * pointer, function and array types.
+ *
+ * The current type is the Type built by deftyp() (dcs->d_type) and
+ * pointer, function and array types already added for this
+ * declarator. The new type extension is inserted between both.
+ */
+sym_t *
+addptr(decl, pi)
+ sym_t *decl;
+ pqinf_t *pi;
+{
+ type_t **tpp, *tp;
+ pqinf_t *npi;
+
+ tpp = &decl->s_type;
+ while (*tpp != dcs->d_type)
+ tpp = &(*tpp)->t_subt;
+
+ while (pi != NULL) {
+ *tpp = tp = getblk(sizeof (type_t));
+ tp->t_tspec = PTR;
+ tp->t_const = pi->p_const;
+ tp->t_volatile = pi->p_volatile;
+ *(tpp = &tp->t_subt) = dcs->d_type;
+ npi = pi->p_nxt;
+ free(pi);
+ pi = npi;
+ }
+ return (decl);
+}
+
+/*
+ * If a dimension was specified, dim is 1, otherwise 0
+ * n is the specified dimension
+ */
+sym_t *
+addarray(decl, dim, n)
+ sym_t *decl;
+ int dim, n;
+{
+ type_t **tpp, *tp;
+
+ tpp = &decl->s_type;
+ while (*tpp != dcs->d_type)
+ tpp = &(*tpp)->t_subt;
+
+ *tpp = tp = getblk(sizeof (type_t));
+ tp->t_tspec = ARRAY;
+ tp->t_subt = dcs->d_type;
+ tp->t_dim = n;
+
+ if (n < 0) {
+ /* zero or negative array dimension */
+ error(20);
+ n = 0;
+ } else if (n == 0 && dim) {
+ /* zero or negative array dimension */
+ warning(20);
+ } else if (n == 0 && !dim) {
+ /* is incomplete type */
+ setcompl(tp, 1);
+ }
+
+ return (decl);
+}
+
+sym_t *
+addfunc(decl, args)
+ sym_t *decl, *args;
+{
+ type_t **tpp, *tp;
+
+ if (dcs->d_proto) {
+ if (tflag)
+ /* function prototypes are illegal in traditional C */
+ warning(270);
+ args = nsfunc(decl, args);
+ } else {
+ osfunc(decl, args);
+ }
+
+ /*
+ * The symbols are removed from the symbol table by popdecl() after
+ * addfunc(). To be able to restore them if this is a function
+ * definition, a pointer to the list of all symbols is stored in
+ * dcs->d_nxt->d_fpsyms. Also a list of the arguments (concatenated
+ * by s_nxt) is stored in dcs->d_nxt->d_fargs.
+ * (dcs->d_nxt must be used because *dcs is the declaration stack
+ * element created for the list of params and is removed after
+ * addfunc())
+ */
+ if (dcs->d_nxt->d_ctx == EXTERN &&
+ decl->s_type == dcs->d_nxt->d_type) {
+ dcs->d_nxt->d_fpsyms = dcs->d_dlsyms;
+ dcs->d_nxt->d_fargs = args;
+ }
+
+ tpp = &decl->s_type;
+ while (*tpp != dcs->d_nxt->d_type)
+ tpp = &(*tpp)->t_subt;
+
+ *tpp = tp = getblk(sizeof (type_t));
+ tp->t_tspec = FUNC;
+ tp->t_subt = dcs->d_nxt->d_type;
+ if ((tp->t_proto = dcs->d_proto) != 0)
+ tp->t_args = args;
+ tp->t_vararg = dcs->d_vararg;
+
+ return (decl);
+}
+
+/*
+ * Called for new style function declarations.
+ */
+/* ARGSUSED */
+static sym_t *
+nsfunc(decl, args)
+ sym_t *decl, *args;
+{
+ sym_t *arg, *sym;
+ scl_t sc;
+ int n;
+
+ /*
+ * Declarations of structs/unions/enums in param lists are legal,
+ * but senseless.
+ */
+ for (sym = dcs->d_dlsyms; sym != NULL; sym = sym->s_dlnxt) {
+ sc = sym->s_scl;
+ if (sc == STRTAG || sc == UNIONTAG || sc == ENUMTAG) {
+ /* dubious tag declaration: %s %s */
+ warning(85, scltoa(sc), sym->s_name);
+ }
+ }
+
+ n = 1;
+ for (arg = args; arg != NULL; arg = arg->s_nxt) {
+ if (arg->s_type->t_tspec == VOID) {
+ if (n > 1 || arg->s_nxt != NULL) {
+ /* "void" must be sole parameter */
+ error(60);
+ arg->s_type = gettyp(INT);
+ }
+ }
+ n++;
+ }
+
+ /* return NULL if first param is VOID */
+ return (args != NULL && args->s_type->t_tspec != VOID ? args : NULL);
+}
+
+/*
+ * Called for old style function declarations.
+ */
+static void
+osfunc(decl, args)
+ sym_t *decl, *args;
+{
+ /*
+ * Remember list of params only if this is really seams to be
+ * a function definition.
+ */
+ if (dcs->d_nxt->d_ctx == EXTERN &&
+ decl->s_type == dcs->d_nxt->d_type) {
+ /*
+ * We assume that this becomes a function definition. If
+ * we are wrong, its corrected in chkfdef().
+ */
+ if (args != NULL) {
+ decl->s_osdef = 1;
+ decl->s_args = args;
+ }
+ } else {
+ if (args != NULL)
+ /* function prototype parameters must have types */
+ warning(62);
+ }
+}
+
+/*
+ * Lists of Identifiers in functions declarations are allowed only if
+ * its also a function definition. If this is not the case, print a
+ * error message.
+ */
+void
+chkfdef(sym, msg)
+ sym_t *sym;
+ int msg;
+{
+ if (sym->s_osdef) {
+ if (msg) {
+ /* incomplete or misplaced function definition */
+ error(22);
+ }
+ sym->s_osdef = 0;
+ sym->s_args = NULL;
+ }
+}
+
+/*
+ * Process the name in a declarator.
+ * If the symbol does already exists, a new one is created.
+ * The symbol becomes one of the storage classes EXTERN, STATIC, AUTO or
+ * TYPEDEF.
+ * s_def and s_reg are valid after dname().
+ */
+sym_t *
+dname(sym)
+ sym_t *sym;
+{
+ scl_t sc;
+
+ if (sym->s_scl == NOSCL) {
+ dcs->d_rdcsym = NULL;
+ } else if (sym->s_defarg) {
+ sym->s_defarg = 0;
+ dcs->d_rdcsym = NULL;
+ } else {
+ dcs->d_rdcsym = sym;
+ sym = pushdown(sym);
+ }
+
+ switch (dcs->d_ctx) {
+ case MOS:
+ case MOU:
+ /* Parent setzen */
+ sym->s_styp = dcs->d_tagtyp->t_str;
+ sym->s_def = DEF;
+ sym->s_value.v_tspec = INT;
+ sc = dcs->d_ctx;
+ break;
+ case EXTERN:
+ /*
+ * static and external symbols without "extern" are
+ * considered to be tentative defined, external
+ * symbols with "extern" are declared, and typedef names
+ * are defined. Tentative defined and declared symbols
+ * may become defined if an initializer is present or
+ * this is a function definition.
+ */
+ if ((sc = dcs->d_scl) == NOSCL) {
+ sc = EXTERN;
+ sym->s_def = TDEF;
+ } else if (sc == STATIC) {
+ sym->s_def = TDEF;
+ } else if (sc == TYPEDEF) {
+ sym->s_def = DEF;
+ } else if (sc == EXTERN) {
+ sym->s_def = DECL;
+ } else {
+ lerror("dname() 1");
+ }
+ break;
+ case PARG:
+ sym->s_arg = 1;
+ /* FALLTHROUGH */
+ case ARG:
+ if ((sc = dcs->d_scl) == NOSCL) {
+ sc = AUTO;
+ } else if (sc == REG) {
+ sym->s_reg = 1;
+ sc = AUTO;
+ } else {
+ lerror("dname() 2");
+ }
+ sym->s_def = DEF;
+ break;
+ case AUTO:
+ if ((sc = dcs->d_scl) == NOSCL) {
+ /*
+ * XXX somewhat ugly because we dont know whether
+ * this is AUTO or EXTERN (functions). If we are
+ * wrong it must be corrected in decl1loc(), where
+ * we have the neccessary type information.
+ */
+ sc = AUTO;
+ sym->s_def = DEF;
+ } else if (sc == AUTO || sc == STATIC || sc == TYPEDEF) {
+ sym->s_def = DEF;
+ } else if (sc == REG) {
+ sym->s_reg = 1;
+ sc = AUTO;
+ sym->s_def = DEF;
+ } else if (sc == EXTERN) {
+ sym->s_def = DECL;
+ } else {
+ lerror("dname() 3");
+ }
+ break;
+ default:
+ lerror("dname() 4");
+ }
+ sym->s_scl = sc;
+
+ sym->s_type = dcs->d_type;
+
+ dcs->d_fpsyms = NULL;
+
+ return (sym);
+}
+
+/*
+ * Process a name in the list of formal params in an old style function
+ * definition.
+ */
+sym_t *
+iname(sym)
+ sym_t *sym;
+{
+ if (sym->s_scl != NOSCL) {
+ if (blklev == sym->s_blklev) {
+ /* redeclaration of formal parameter %s */
+ error(21, sym->s_name);
+ if (!sym->s_defarg)
+ lerror("iname()");
+ }
+ sym = pushdown(sym);
+ }
+ sym->s_type = gettyp(INT);
+ sym->s_scl = AUTO;
+ sym->s_def = DEF;
+ sym->s_defarg = sym->s_arg = 1;
+ return (sym);
+}
+
+/*
+ * Create the type of a tag.
+ *
+ * tag points to the symbol table entry of the tag
+ * kind is the kind of the tag (STRUCT/UNION/ENUM)
+ * decl is 1 if the type of the tag will be completed in this declaration
+ * (the following token is T_LBRACE)
+ * semi is 1 if the following token is T_SEMI
+ */
+type_t *
+mktag(tag, kind, decl, semi)
+ sym_t *tag;
+ tspec_t kind;
+ int decl, semi;
+{
+ scl_t scl;
+ type_t *tp;
+
+ if (kind == STRUCT) {
+ scl = STRTAG;
+ } else if (kind == UNION) {
+ scl = UNIONTAG;
+ } else if (kind == ENUM) {
+ scl = ENUMTAG;
+ } else {
+ lerror("mktag()");
+ }
+
+ if (tag != NULL) {
+ if (tag->s_scl != NOSCL) {
+ tag = newtag(tag, scl, decl, semi);
+ } else {
+ /* a new tag, no empty declaration */
+ dcs->d_nxt->d_nedecl = 1;
+ if (scl == ENUMTAG && !decl) {
+ if (!tflag && (sflag || pflag))
+ /* forward reference to enum type */
+ warning(42);
+ }
+ }
+ if (tag->s_scl == NOSCL) {
+ tag->s_scl = scl;
+ tag->s_type = tp = getblk(sizeof (type_t));
+ } else {
+ tp = tag->s_type;
+ }
+ } else {
+ tag = getblk(sizeof (sym_t));
+ tag->s_name = unnamed;
+ STRUCT_ASSIGN(tag->s_dpos, curr_pos);
+ tag->s_kind = FTAG;
+ tag->s_scl = scl;
+ tag->s_blklev = -1;
+ tag->s_type = tp = getblk(sizeof (type_t));
+ dcs->d_nxt->d_nedecl = 1;
+ }
+
+ if (tp->t_tspec == NOTSPEC) {
+ tp->t_tspec = kind;
+ if (kind != ENUM) {
+ tp->t_str = getblk(sizeof (str_t));
+ tp->t_str->align = CHAR_BIT;
+ tp->t_str->stag = tag;
+ } else {
+ tp->t_isenum = 1;
+ tp->t_enum = getblk(sizeof (enum_t));
+ tp->t_enum->etag = tag;
+ }
+ /* ist unvollstaendiger Typ */
+ setcompl(tp, 1);
+ }
+
+ return (tp);
+}
+
+/*
+ * Checks all possible cases of tag redeclarations.
+ * decl is 1 if T_LBRACE follows
+ * semi is 1 if T_SEMI follows
+ */
+static sym_t *
+newtag(tag, scl, decl, semi)
+ sym_t *tag;
+ scl_t scl;
+ int decl, semi;
+{
+ if (tag->s_blklev < blklev) {
+ if (semi) {
+ /* "struct a;" */
+ if (!tflag) {
+ if (!sflag)
+ /* decl. introduces new type ... */
+ warning(44, scltoa(scl), tag->s_name);
+ tag = pushdown(tag);
+ } else if (tag->s_scl != scl) {
+ /* base type is really "%s %s" */
+ warning(45, scltoa(tag->s_scl), tag->s_name);
+ }
+ dcs->d_nxt->d_nedecl = 1;
+ } else if (decl) {
+ /* "struct a { ..." */
+ if (hflag)
+ /* redefinition hides earlier one: %s */
+ warning(43, tag->s_name);
+ tag = pushdown(tag);
+ dcs->d_nxt->d_nedecl = 1;
+ } else if (tag->s_scl != scl) {
+ /* base type is really "%s %s" */
+ warning(45, scltoa(tag->s_scl), tag->s_name);
+ /* declaration introduces new type in ANSI C: %s %s */
+ if (!sflag)
+ warning(44, scltoa(scl), tag->s_name);
+ tag = pushdown(tag);
+ dcs->d_nxt->d_nedecl = 1;
+ }
+ } else {
+ if (tag->s_scl != scl) {
+ /* (%s) tag redeclared */
+ error(46, scltoa(tag->s_scl));
+ prevdecl(-1, tag);
+ tag = pushdown(tag);
+ dcs->d_nxt->d_nedecl = 1;
+ } else if (decl && !incompl(tag->s_type)) {
+ /* (%s) tag redeclared */
+ error(46, scltoa(tag->s_scl));
+ prevdecl(-1, tag);
+ tag = pushdown(tag);
+ dcs->d_nxt->d_nedecl = 1;
+ } else if (semi || decl) {
+ dcs->d_nxt->d_nedecl = 1;
+ }
+ }
+ return (tag);
+}
+
+const char *
+scltoa(sc)
+ scl_t sc;
+{
+ const char *s;
+
+ switch (sc) {
+ case EXTERN: s = "extern"; break;
+ case STATIC: s = "static"; break;
+ case AUTO: s = "auto"; break;
+ case REG: s = "register"; break;
+ case TYPEDEF: s = "typedef"; break;
+ case STRTAG: s = "struct"; break;
+ case UNIONTAG: s = "union"; break;
+ case ENUMTAG: s = "enum"; break;
+ default: lerror("tagttoa()");
+ }
+ return (s);
+}
+
+/*
+ * Completes the type of a tag in a struct/union/enum declaration.
+ * tp points to the type of the, tag, fmem to the list of members/enums.
+ */
+type_t *
+compltag(tp, fmem)
+ type_t *tp;
+ sym_t *fmem;
+{
+ tspec_t t;
+ str_t *sp;
+ int n;
+ sym_t *mem;
+
+ /* from now a complete type */
+ setcompl(tp, 0);
+
+ if ((t = tp->t_tspec) != ENUM) {
+ align(dcs->d_stralign, 0);
+ sp = tp->t_str;
+ sp->align = dcs->d_stralign;
+ sp->size = dcs->d_offset;
+ sp->memb = fmem;
+ if (sp->size == 0) {
+ /* zero sized %s */
+ (void)gnuism(47, ttab[t].tt_name);
+ } else {
+ n = 0;
+ for (mem = fmem; mem != NULL; mem = mem->s_nxt) {
+ if (mem->s_name != unnamed)
+ n++;
+ }
+ if (n == 0) {
+ /* %s has no named members */
+ warning(65,
+ t == STRUCT ? "structure" : "union");
+ }
+ }
+ } else {
+ tp->t_enum->elem = fmem;
+ }
+ return (tp);
+}
+
+/*
+ * Processes the name of an enumerator in en enum declaration.
+ *
+ * sym points to the enumerator
+ * val is the value of the enumerator
+ * impl is 1 if the the value of the enumerator was not explicit specified.
+ */
+sym_t *
+ename(sym, val, impl)
+ sym_t *sym;
+ int val, impl;
+{
+ if (sym->s_scl) {
+ if (sym->s_blklev == blklev) {
+ /* no hflag, because this is illegal!!! */
+ if (sym->s_arg) {
+ /* enumeration constant hides parameter: %s */
+ warning(57, sym->s_name);
+ } else {
+ /* redeclaration of %s */
+ error(27, sym->s_name);
+ /*
+ * inside blocks it should not too complicated
+ * to find the position of the previous
+ * declaration
+ */
+ if (blklev == 0)
+ prevdecl(-1, sym);
+ }
+ } else {
+ if (hflag)
+ /* redefinition hides earlier one: %s */
+ warning(43, sym->s_name);
+ }
+ sym = pushdown(sym);
+ }
+ sym->s_scl = ENUMCON;
+ sym->s_type = dcs->d_tagtyp;
+ sym->s_value.v_tspec = INT;
+ sym->s_value.v_quad = val;
+ if (impl && val - 1 == INT_MAX) {
+ /* overflow in enumeration values: %s */
+ warning(48, sym->s_name);
+ }
+ enumval = val + 1;
+ return (sym);
+}
+
+/*
+ * Process a single external declarator.
+ */
+void
+decl1ext(dsym, initflg)
+ sym_t *dsym;
+ int initflg;
+{
+ int warn, rval, redec;
+ sym_t *rdsym;
+
+ chkfdef(dsym, 1);
+
+ chktyp(dsym);
+
+ if (initflg && !(initerr = chkinit(dsym)))
+ dsym->s_def = DEF;
+
+ /*
+ * Declarations of functions are marked as "tentative" in dname().
+ * This is wrong because there are no tentative function
+ * definitions.
+ */
+ if (dsym->s_type->t_tspec == FUNC && dsym->s_def == TDEF)
+ dsym->s_def = DECL;
+
+ if (dcs->d_inline) {
+ if (dsym->s_type->t_tspec == FUNC) {
+ dsym->s_inline = 1;
+ } else {
+ /* variable declared inline: %s */
+ warning(268, dsym->s_name);
+ }
+ }
+
+ /* Write the declaration into the output file */
+ if (plibflg && llibflg &&
+ dsym->s_type->t_tspec == FUNC && dsym->s_type->t_proto) {
+ /*
+ * With both LINTLIBRARY and PROTOLIB the prototyp is
+ * written as a function definition to the output file.
+ */
+ rval = dsym->s_type->t_subt->t_tspec != VOID;
+ outfdef(dsym, &dsym->s_dpos, rval, 0, NULL);
+ } else {
+ outsym(dsym, dsym->s_scl, dsym->s_def);
+ }
+
+ if ((rdsym = dcs->d_rdcsym) != NULL) {
+
+ /*
+ * If the old symbol stems from a old style function definition
+ * we have remembered the params in rdsmy->s_args and compare
+ * them with the params of the prototype.
+ */
+ if (rdsym->s_osdef && dsym->s_type->t_proto) {
+ redec = chkosdef(rdsym, dsym);
+ } else {
+ redec = 0;
+ }
+
+ if (!redec && !isredec(dsym, (warn = 0, &warn))) {
+
+ if (warn) {
+ /* redeclaration of %s */
+ (*(sflag ? error : warning))(27, dsym->s_name);
+ prevdecl(-1, rdsym);
+ }
+
+ /*
+ * Overtake the rememberd params if the new symbol
+ * is not a prototype.
+ */
+ if (rdsym->s_osdef && !dsym->s_type->t_proto) {
+ dsym->s_osdef = rdsym->s_osdef;
+ dsym->s_args = rdsym->s_args;
+ STRUCT_ASSIGN(dsym->s_dpos, rdsym->s_dpos);
+ }
+
+ /*
+ * Remember the position of the declaration if the
+ * old symbol was a prototype and the new is not.
+ * Also remember the position if the old symbol
+ * was defined and the new is not.
+ */
+ if (rdsym->s_type->t_proto && !dsym->s_type->t_proto) {
+ STRUCT_ASSIGN(dsym->s_dpos, rdsym->s_dpos);
+ } else if (rdsym->s_def == DEF && dsym->s_def != DEF) {
+ STRUCT_ASSIGN(dsym->s_dpos, rdsym->s_dpos);
+ }
+
+ /*
+ * Copy informations about usage of the name into
+ * the new symbol.
+ */
+ cpuinfo(dsym, rdsym);
+
+ /* Once a name is defined, it remains defined. */
+ if (rdsym->s_def == DEF)
+ dsym->s_def = DEF;
+
+ /* once a function is inline, it remains inline */
+ if (rdsym->s_inline)
+ dsym->s_inline = 1;
+
+ compltyp(dsym, rdsym);
+
+ }
+
+ rmsym(rdsym);
+ }
+
+ if (dsym->s_scl == TYPEDEF) {
+ dsym->s_type = duptyp(dsym->s_type);
+ dsym->s_type->t_typedef = 1;
+ settdsym(dsym->s_type, dsym);
+ }
+
+}
+
+/*
+ * Copies informations about usage into a new symbol table entry of
+ * the same symbol.
+ */
+void
+cpuinfo(sym, rdsym)
+ sym_t *sym, *rdsym;
+{
+ sym->s_spos = rdsym->s_spos;
+ sym->s_upos = rdsym->s_upos;
+ sym->s_set = rdsym->s_set;
+ sym->s_used = rdsym->s_used;
+}
+
+/*
+ * Prints an error and returns 1 if a symbol is redeclared/redefined.
+ * Otherwise returns 0 and, in some cases of minor problems, prints
+ * a warning.
+ */
+int
+isredec(dsym, warn)
+ sym_t *dsym;
+ int *warn;
+{
+ sym_t *rsym;
+
+ if ((rsym = dcs->d_rdcsym)->s_scl == ENUMCON) {
+ /* redeclaration of %s */
+ error(27, dsym->s_name);
+ prevdecl(-1, rsym);
+ return (1);
+ }
+ if (rsym->s_scl == TYPEDEF) {
+ /* typedef redeclared: %s */
+ error(89, dsym->s_name);
+ prevdecl(-1, rsym);
+ return (1);
+ }
+ if (dsym->s_scl == TYPEDEF) {
+ /* redeclaration of %s */
+ error(27, dsym->s_name);
+ prevdecl(-1, rsym);
+ return (1);
+ }
+ if (rsym->s_def == DEF && dsym->s_def == DEF) {
+ /* redefinition of %s */
+ error(28, dsym->s_name);
+ prevdecl(-1, rsym);
+ return(1);
+ }
+ if (!eqtype(rsym->s_type, dsym->s_type, 0, 0, warn)) {
+ /* redeclaration of %s */
+ error(27, dsym->s_name);
+ prevdecl(-1, rsym);
+ return(1);
+ }
+ if (rsym->s_scl == EXTERN && dsym->s_scl == EXTERN)
+ return(0);
+ if (rsym->s_scl == STATIC && dsym->s_scl == STATIC)
+ return(0);
+ if (rsym->s_scl == STATIC && dsym->s_def == DECL)
+ return(0);
+ if (rsym->s_scl == EXTERN && rsym->s_def == DEF) {
+ /*
+ * All cases except "int a = 1; static int a;" are catched
+ * above with or without a warning
+ */
+ /* redeclaration of %s */
+ error(27, dsym->s_name);
+ prevdecl(-1, rsym);
+ return(1);
+ }
+ if (rsym->s_scl == EXTERN) {
+ /* previously declared extern, becomes static: %s */
+ warning(29, dsym->s_name);
+ prevdecl(-1, rsym);
+ return(0);
+ }
+ /*
+ * Now its on of:
+ * "static a; int a;", "static a; int a = 1;", "static a = 1; int a;"
+ */
+ /* redeclaration of %s; ANSI C requires "static" */
+ if (sflag) {
+ warning(30, dsym->s_name);
+ prevdecl(-1, rsym);
+ }
+ dsym->s_scl = STATIC;
+ return (0);
+}
+
+/*
+ * Checks if two types are compatible. Returns 0 if not, otherwise 1.
+ *
+ * ignqual ignore qualifiers of type; used for function params
+ * promot promote left type; used for comparision of params of
+ * old style function definitions with params of prototypes.
+ * *warn set to 1 if an old style function declaration is not
+ * compatible with a prototype
+ */
+int
+eqtype(tp1, tp2, ignqual, promot, warn)
+ type_t *tp1, *tp2;
+ int ignqual, promot, *warn;
+{
+ tspec_t t;
+
+ while (tp1 != NULL && tp2 != NULL) {
+
+ t = tp1->t_tspec;
+ if (promot) {
+ if (t == FLOAT) {
+ t = DOUBLE;
+ } else if (t == CHAR || t == SCHAR) {
+ t = INT;
+ } else if (t == UCHAR) {
+ t = tflag ? UINT : INT;
+ } else if (t == SHORT) {
+ t = INT;
+ } else if (t == USHORT) {
+ /* CONSTCOND */
+ t = INT_MAX < USHRT_MAX || tflag ? UINT : INT;
+ }
+ }
+
+ if (t != tp2->t_tspec)
+ return (0);
+
+ if (tp1->t_const != tp2->t_const && !ignqual && !tflag)
+ return (0);
+
+ if (tp1->t_volatile != tp2->t_volatile && !ignqual && !tflag)
+ return (0);
+
+ if (t == STRUCT || t == UNION)
+ return (tp1->t_str == tp2->t_str);
+
+ if (t == ARRAY && tp1->t_dim != tp2->t_dim) {
+ if (tp1->t_dim != 0 && tp2->t_dim != 0)
+ return (0);
+ }
+
+ /* dont check prototypes for traditional */
+ if (t == FUNC && !tflag) {
+ if (tp1->t_proto && tp2->t_proto) {
+ if (!eqargs(tp1, tp2, warn))
+ return (0);
+ } else if (tp1->t_proto) {
+ if (!mnoarg(tp1, warn))
+ return (0);
+ } else if (tp2->t_proto) {
+ if (!mnoarg(tp2, warn))
+ return (0);
+ }
+ }
+
+ tp1 = tp1->t_subt;
+ tp2 = tp2->t_subt;
+ ignqual = promot = 0;
+
+ }
+
+ return (tp1 == tp2);
+}
+
+/*
+ * Compares the parameter types of two prototypes.
+ */
+static int
+eqargs(tp1, tp2, warn)
+ type_t *tp1, *tp2;
+ int *warn;
+{
+ sym_t *a1, *a2;
+
+ if (tp1->t_vararg != tp2->t_vararg)
+ return (0);
+
+ a1 = tp1->t_args;
+ a2 = tp2->t_args;
+
+ while (a1 != NULL && a2 != NULL) {
+
+ if (eqtype(a1->s_type, a2->s_type, 1, 0, warn) == 0)
+ return (0);
+
+ a1 = a1->s_nxt;
+ a2 = a2->s_nxt;
+
+ }
+
+ return (a1 == a2);
+}
+
+/*
+ * mnoarg() (matches functions with no argument type information)
+ * returns 1 if all parameters of a prototype are compatible with
+ * and old style function declaration.
+ * This is the case if following conditions are met:
+ * 1. the prototype must have a fixed number of parameters
+ * 2. no parameter is of type float
+ * 3. no parameter is converted to another type if integer promotion
+ * is applied on it
+ */
+static int
+mnoarg(tp, warn)
+ type_t *tp;
+ int *warn;
+{
+ sym_t *arg;
+ tspec_t t;
+
+ if (tp->t_vararg) {
+ if (warn != NULL)
+ *warn = 1;
+ }
+ for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt) {
+ if ((t = arg->s_type->t_tspec) == FLOAT ||
+ t == CHAR || t == SCHAR || t == UCHAR ||
+ t == SHORT || t == USHORT) {
+ if (warn != NULL)
+ *warn = 1;
+ }
+ }
+ return (1);
+}
+
+/*
+ * Compares a prototype declaration with the remembered arguments of
+ * a previous old style function definition.
+ */
+static int
+chkosdef(rdsym, dsym)
+ sym_t *rdsym, *dsym;
+{
+ sym_t *args, *pargs, *arg, *parg;
+ int narg, nparg, n;
+ int warn, msg;
+
+ args = rdsym->s_args;
+ pargs = dsym->s_type->t_args;
+
+ msg = 0;
+
+ narg = nparg = 0;
+ for (arg = args; arg != NULL; arg = arg->s_nxt)
+ narg++;
+ for (parg = pargs; parg != NULL; parg = parg->s_nxt)
+ nparg++;
+ if (narg != nparg) {
+ /* prototype does not match old-style definition */
+ error(63);
+ msg = 1;
+ goto end;
+ }
+
+ arg = args;
+ parg = pargs;
+ n = 1;
+ while (narg--) {
+ warn = 0;
+ /*
+ * If it does not match due to promotion and sflag is
+ * not set we print only a warning.
+ */
+ if (!eqtype(arg->s_type, parg->s_type, 1, 1, &warn) || warn) {
+ /* prototype does not match old-style def., arg #%d */
+ error(299, n);
+ msg = 1;
+ }
+ arg = arg->s_nxt;
+ parg = parg->s_nxt;
+ n++;
+ }
+
+ end:
+ if (msg)
+ /* old style definition */
+ prevdecl(300, rdsym);
+
+ return (msg);
+}
+
+/*
+ * Complets a type by copying the dimension and prototype information
+ * from a second compatible type.
+ *
+ * Following lines are legal:
+ * "typedef a[]; a b; a b[10]; a c; a c[20];"
+ * "typedef ft(); ft f; f(int); ft g; g(long);"
+ * This means that, if a type is completed, the type structure must
+ * be duplicated.
+ */
+void
+compltyp(dsym, ssym)
+ sym_t *dsym, *ssym;
+{
+ type_t **dstp, *src;
+ type_t *dst;
+
+ dstp = &dsym->s_type;
+ src = ssym->s_type;
+
+ while ((dst = *dstp) != NULL) {
+ if (src == NULL || dst->t_tspec != src->t_tspec)
+ lerror("compltyp() 1");
+ if (dst->t_tspec == ARRAY) {
+ if (dst->t_dim == 0 && src->t_dim != 0) {
+ *dstp = dst = duptyp(dst);
+ dst->t_dim = src->t_dim;
+ /* now a complete Typ */
+ setcompl(dst, 0);
+ }
+ } else if (dst->t_tspec == FUNC) {
+ if (!dst->t_proto && src->t_proto) {
+ *dstp = dst = duptyp(dst);
+ dst->t_proto = 1;
+ dst->t_args = src->t_args;
+ }
+ }
+ dstp = &dst->t_subt;
+ src = src->t_subt;
+ }
+}
+
+/*
+ * Completes the declaration of a single argument.
+ */
+sym_t *
+decl1arg(sym, initflg)
+ sym_t *sym;
+ int initflg;
+{
+ tspec_t t;
+
+ chkfdef(sym, 1);
+
+ chktyp(sym);
+
+ if (dcs->d_rdcsym != NULL && dcs->d_rdcsym->s_blklev == blklev) {
+ /* redeclaration of formal parameter %s */
+ error(237, sym->s_name);
+ rmsym(dcs->d_rdcsym);
+ sym->s_arg = 1;
+ }
+
+ if (!sym->s_arg) {
+ /* declared argument %s is missing */
+ error(53, sym->s_name);
+ sym->s_arg = 1;
+ }
+
+ if (initflg) {
+ /* cannot initialize parameter: %s */
+ error(52, sym->s_name);
+ initerr = 1;
+ }
+
+ if ((t = sym->s_type->t_tspec) == ARRAY) {
+ sym->s_type = incref(sym->s_type->t_subt, PTR);
+ } else if (t == FUNC) {
+ if (tflag)
+ /* a function is declared as an argument: %s */
+ warning(50, sym->s_name);
+ sym->s_type = incref(sym->s_type, PTR);
+ } else if (t == FLOAT) {
+ if (tflag)
+ sym->s_type = gettyp(DOUBLE);
+ }
+
+ if (dcs->d_inline)
+ /* argument declared inline: %s */
+ warning(269, sym->s_name);
+
+ /*
+ * Arguments must have complete types. lengths() prints the needed
+ * error messages (null dimension is impossible because arrays are
+ * converted to pointers).
+ */
+ if (sym->s_type->t_tspec != VOID)
+ (void)length(sym->s_type, sym->s_name);
+
+ setsflg(sym);
+
+ return (sym);
+}
+
+/*
+ * Does some checks for lint directives which apply to functions.
+ * Processes arguments in old style function definitions which default
+ * to int.
+ * Checks compatiblility of old style function definition with previous
+ * prototype.
+ */
+void
+cluparg()
+{
+ sym_t *args, *arg, *pargs, *parg;
+ int narg, nparg, n, msg;
+ tspec_t t;
+
+ args = funcsym->s_args;
+ pargs = funcsym->s_type->t_args;
+
+ /* check for illegal combinations of lint directives */
+ if (prflstrg != -1 && scflstrg != -1) {
+ /* can't be used together: ** PRINTFLIKE ** ** SCANFLIKE ** */
+ warning(289);
+ prflstrg = scflstrg = -1;
+ }
+ if (nvararg != -1 && (prflstrg != -1 || scflstrg != -1)) {
+ /* dubious use of ** VARARGS ** with ** %s ** */
+ warning(288, prflstrg != -1 ? "PRINTFLIKE" : "SCANFLIKE");
+ nvararg = -1;
+ }
+
+ /*
+ * check if the argument of a lint directive is compatible with the
+ * number of arguments.
+ */
+ narg = 0;
+ for (arg = dcs->d_fargs; arg != NULL; arg = arg->s_nxt)
+ narg++;
+ if (nargusg > narg) {
+ /* argument number mismatch with directive: ** %s ** */
+ warning(283, "ARGSUSED");
+ nargusg = 0;
+ }
+ if (nvararg > narg) {
+ /* argument number mismatch with directive: ** %s ** */
+ warning(283, "VARARGS");
+ nvararg = 0;
+ }
+ if (prflstrg > narg) {
+ /* argument number mismatch with directive: ** %s ** */
+ warning(283, "PRINTFLIKE");
+ prflstrg = -1;
+ } else if (prflstrg == 0) {
+ prflstrg = -1;
+ }
+ if (scflstrg > narg) {
+ /* argument number mismatch with directive: ** %s ** */
+ warning(283, "SCANFLIKE");
+ scflstrg = -1;
+ } else if (scflstrg == 0) {
+ scflstrg = -1;
+ }
+ if (prflstrg != -1 || scflstrg != -1) {
+ narg = prflstrg != -1 ? prflstrg : scflstrg;
+ arg = dcs->d_fargs;
+ for (n = 1; n < narg; n++)
+ arg = arg->s_nxt;
+ if (arg->s_type->t_tspec != PTR ||
+ ((t = arg->s_type->t_subt->t_tspec) != CHAR &&
+ t != UCHAR && t != SCHAR)) {
+ /* arg. %d must be 'char *' for PRINTFLIKE/SCANFLIKE */
+ warning(293, narg);
+ prflstrg = scflstrg = -1;
+ }
+ }
+
+ /*
+ * print a warning for each argument off an old style function
+ * definition which defaults to int
+ */
+ for (arg = args; arg != NULL; arg = arg->s_nxt) {
+ if (arg->s_defarg) {
+ /* argument type defaults to int: %s */
+ warning(32, arg->s_name);
+ arg->s_defarg = 0;
+ setsflg(arg);
+ }
+ }
+
+ /*
+ * If this is an old style function definition and a prototyp
+ * exists, compare the types of arguments.
+ */
+ if (funcsym->s_osdef && funcsym->s_type->t_proto) {
+ /*
+ * If the number of arguments does not macht, we need not
+ * continue.
+ */
+ narg = nparg = 0;
+ msg = 0;
+ for (parg = pargs; parg != NULL; parg = parg->s_nxt)
+ nparg++;
+ for (arg = args; arg != NULL; arg = arg->s_nxt)
+ narg++;
+ if (narg != nparg) {
+ /* parameter mismatch: %d declared, %d defined */
+ error(51, nparg, narg);
+ msg = 1;
+ } else {
+ parg = pargs;
+ arg = args;
+ while (narg--) {
+ msg |= chkptdecl(arg, parg);
+ parg = parg->s_nxt;
+ arg = arg->s_nxt;
+ }
+ }
+ if (msg)
+ /* prototype declaration */
+ prevdecl(285, dcs->d_rdcsym);
+
+ /* from now the prototype is valid */
+ funcsym->s_osdef = 0;
+ funcsym->s_args = NULL;
+
+ }
+
+}
+
+/*
+ * Checks compatibility of an old style function definition with a previous
+ * prototype declaration.
+ * Returns 1 if the position of the previous declaration should be reported.
+ */
+static int
+chkptdecl(arg, parg)
+ sym_t *arg, *parg;
+{
+ type_t *tp, *ptp;
+ int warn, msg;
+
+ tp = arg->s_type;
+ ptp = parg->s_type;
+
+ msg = 0;
+ warn = 0;
+
+ if (!eqtype(tp, ptp, 1, 1, &warn)) {
+ if (eqtype(tp, ptp, 1, 0, &warn)) {
+ /* type does not match prototype: %s */
+ msg = gnuism(58, arg->s_name);
+ } else {
+ /* type does not match prototype: %s */
+ error(58, arg->s_name);
+ msg = 1;
+ }
+ } else if (warn) {
+ /* type does not match prototype: %s */
+ (*(sflag ? error : warning))(58, arg->s_name);
+ msg = 1;
+ }
+
+ return (msg);
+}
+
+/*
+ * Completes a single local declaration/definition.
+ */
+void
+decl1loc(dsym, initflg)
+ sym_t *dsym;
+ int initflg;
+{
+ /* Correct a mistake done in dname(). */
+ if (dsym->s_type->t_tspec == FUNC) {
+ dsym->s_def = DECL;
+ if (dcs->d_scl == NOSCL)
+ dsym->s_scl = EXTERN;
+ }
+
+ if (dsym->s_type->t_tspec == FUNC) {
+ if (dsym->s_scl == STATIC) {
+ /* dubious static function at block level: %s */
+ warning(93, dsym->s_name);
+ dsym->s_scl = EXTERN;
+ } else if (dsym->s_scl != EXTERN && dsym->s_scl != TYPEDEF) {
+ /* function has illegal storage class: %s */
+ error(94, dsym->s_name);
+ dsym->s_scl = EXTERN;
+ }
+ }
+
+ /*
+ * functions may be declared inline at local scope, although
+ * this has no effect for a later definition of the same
+ * function.
+ * XXX it should have an effect if tflag is set. this would
+ * also be the way gcc behaves.
+ */
+ if (dcs->d_inline) {
+ if (dsym->s_type->t_tspec == FUNC) {
+ dsym->s_inline = 1;
+ } else {
+ /* variable declared inline: %s */
+ warning(268, dsym->s_name);
+ }
+ }
+
+ chkfdef(dsym, 1);
+
+ chktyp(dsym);
+
+ if (dcs->d_rdcsym != NULL && dsym->s_scl == EXTERN)
+ ledecl(dsym);
+
+ if (dsym->s_scl == EXTERN) {
+ /*
+ * XXX wenn die statische Variable auf Ebene 0 erst
+ * spaeter definiert wird, haben wir die Brille auf.
+ */
+ if (dsym->s_xsym == NULL) {
+ outsym(dsym, EXTERN, dsym->s_def);
+ } else {
+ outsym(dsym, dsym->s_xsym->s_scl, dsym->s_def);
+ }
+ }
+
+ if (dcs->d_rdcsym != NULL) {
+
+ if (dcs->d_rdcsym->s_blklev == 0) {
+
+ switch (dsym->s_scl) {
+ case AUTO:
+ /* automatic hides external declaration: %s */
+ if (hflag)
+ warning(86, dsym->s_name);
+ break;
+ case STATIC:
+ /* static hides external declaration: %s */
+ if (hflag)
+ warning(87, dsym->s_name);
+ break;
+ case TYPEDEF:
+ /* typedef hides external declaration: %s */
+ if (hflag)
+ warning(88, dsym->s_name);
+ break;
+ case EXTERN:
+ /*
+ * Warnings and errors are printed in ledecl()
+ */
+ break;
+ default:
+ lerror("decl1loc() 1");
+ }
+
+ } else if (dcs->d_rdcsym->s_blklev == blklev) {
+
+ /* no hflag, because its illegal! */
+ if (dcs->d_rdcsym->s_arg) {
+ /*
+ * if !tflag, a "redeclaration of %s" error
+ * is produced below
+ */
+ if (tflag) {
+ if (hflag)
+ /* decl. hides parameter: %s */
+ warning(91, dsym->s_name);
+ rmsym(dcs->d_rdcsym);
+ }
+ }
+
+ } else if (dcs->d_rdcsym->s_blklev < blklev) {
+
+ if (hflag)
+ /* declaration hides earlier one: %s */
+ warning(95, dsym->s_name);
+
+ }
+
+ if (dcs->d_rdcsym->s_blklev == blklev) {
+
+ /* redeclaration of %s */
+ error(27, dsym->s_name);
+ rmsym(dcs->d_rdcsym);
+
+ }
+
+ }
+
+ if (initflg && !(initerr = chkinit(dsym))) {
+ dsym->s_def = DEF;
+ setsflg(dsym);
+ }
+
+ if (dsym->s_scl == TYPEDEF) {
+ dsym->s_type = duptyp(dsym->s_type);
+ dsym->s_type->t_typedef = 1;
+ settdsym(dsym->s_type, dsym);
+ }
+
+ /*
+ * Before we can check the size we must wait for a initialisation
+ * which may follow.
+ */
+}
+
+/*
+ * Processes (re)declarations of external Symbols inside blocks.
+ */
+static void
+ledecl(dsym)
+ sym_t *dsym;
+{
+ int eqt, warn;
+ sym_t *esym;
+
+ /* look for a symbol with the same name */
+ esym = dcs->d_rdcsym;
+ while (esym != NULL && esym->s_blklev != 0) {
+ while ((esym = esym->s_link) != NULL) {
+ if (esym->s_kind != FVFT)
+ continue;
+ if (strcmp(dsym->s_name, esym->s_name) == 0)
+ break;
+ }
+ }
+ if (esym == NULL)
+ return;
+ if (esym->s_scl != EXTERN && esym->s_scl != STATIC) {
+ /* gcc accepts this without a warning, pcc prints an error. */
+ /* redeclaration of %s */
+ warning(27, dsym->s_name);
+ prevdecl(-1, esym);
+ return;
+ }
+
+ warn = 0;
+ eqt = eqtype(esym->s_type, dsym->s_type, 0, 0, &warn);
+
+ if (!eqt || warn) {
+ if (esym->s_scl == EXTERN) {
+ /* inconsistent redeclaration of extern: %s */
+ warning(90, dsym->s_name);
+ prevdecl(-1, esym);
+ } else {
+ /* inconsistent redeclaration of static: %s */
+ warning(92, dsym->s_name);
+ prevdecl(-1, esym);
+ }
+ }
+
+ if (eqt) {
+ /*
+ * Remember the external symbol so we can update usage
+ * information at the end of the block.
+ */
+ dsym->s_xsym = esym;
+ }
+}
+
+/*
+ * Print an error or a warning if the symbol cant be initialized due
+ * to type/storage class. Returnvalue is 1 if an error has been
+ * detected.
+ */
+static int
+chkinit(sym)
+ sym_t *sym;
+{
+ int err;
+
+ err = 0;
+
+ if (sym->s_type->t_tspec == FUNC) {
+ /* cannot initialize function: %s */
+ error(24, sym->s_name);
+ err = 1;
+ } else if (sym->s_scl == TYPEDEF) {
+ /* cannot initialize typedef: %s */
+ error(25, sym->s_name);
+ err = 1;
+ } else if (sym->s_scl == EXTERN && sym->s_def == DECL) {
+ /* cannot initialize "extern" declaration: %s */
+ if (dcs->d_ctx == EXTERN) {
+ warning(26, sym->s_name);
+ } else {
+ error(26, sym->s_name);
+ err = 1;
+ }
+ }
+
+ return (err);
+}
+
+/*
+ * Create a symbole for an abstract declaration.
+ */
+sym_t *
+aname()
+{
+ sym_t *sym;
+
+ if (dcs->d_ctx != ABSTRACT && dcs->d_ctx != PARG)
+ lerror("aname()");
+
+ sym = getblk(sizeof (sym_t));
+
+ sym->s_name = unnamed;
+ sym->s_def = DEF;
+ sym->s_scl = ABSTRACT;
+ sym->s_blklev = -1;
+
+ if (dcs->d_ctx == PARG)
+ sym->s_arg = 1;
+
+ sym->s_type = dcs->d_type;
+ dcs->d_rdcsym = NULL;
+ dcs->d_vararg = 0;
+
+ return (sym);
+}
+
+/*
+ * Removes anything which has nothing to do on global level.
+ */
+void
+globclup()
+{
+ while (dcs->d_nxt != NULL)
+ popdecl();
+
+ cleanup();
+ blklev = 0;
+ mblklev = 0;
+
+ /*
+ * remove all informations about pending lint directives without
+ * warnings.
+ */
+ glclup(1);
+}
+
+/*
+ * Process an abstract type declaration
+ */
+sym_t *
+decl1abs(sym)
+ sym_t *sym;
+{
+ chkfdef(sym, 1);
+ chktyp(sym);
+ return (sym);
+}
+
+/*
+ * Checks size after declarations of variables and their initialisation.
+ */
+void
+chksz(dsym)
+ sym_t *dsym;
+{
+ /*
+ * check size only for symbols which are defined and no function and
+ * not typedef name
+ */
+ if (dsym->s_def != DEF)
+ return;
+ if (dsym->s_scl == TYPEDEF)
+ return;
+ if (dsym->s_type->t_tspec == FUNC)
+ return;
+
+ if (length(dsym->s_type, dsym->s_name) == 0 &&
+ dsym->s_type->t_tspec == ARRAY && dsym->s_type->t_dim == 0) {
+ /* empty array declaration: %s */
+ if (tflag) {
+ warning(190, dsym->s_name);
+ } else {
+ error(190, dsym->s_name);
+ }
+ }
+}
+
+/*
+ * Mark an object as set if it is not already
+ */
+void
+setsflg(sym)
+ sym_t *sym;
+{
+ if (!sym->s_set) {
+ sym->s_set = 1;
+ STRUCT_ASSIGN(sym->s_spos, curr_pos);
+ }
+}
+
+/*
+ * Mark an object as used if it is not already
+ */
+void
+setuflg(sym, fcall, szof)
+ sym_t *sym;
+ int fcall, szof;
+{
+ if (!sym->s_used) {
+ sym->s_used = 1;
+ STRUCT_ASSIGN(sym->s_upos, curr_pos);
+ }
+ /*
+ * for function calls another record is written
+ *
+ * XXX Should symbols used in sizeof() treated as used or not?
+ * Probably not, because there is no sense to declare an
+ * external variable only to get their size.
+ */
+ if (!fcall && !szof && sym->s_kind == FVFT && sym->s_scl == EXTERN)
+ outusg(sym);
+}
+
+/*
+ * Prints warnings for a list of variables and labels (concatenated
+ * with s_dlnxt) if these are not used or only set.
+ */
+void
+chkusage(di)
+ dinfo_t *di;
+{
+ sym_t *sym;
+ int mknowarn;
+
+ /* for this warnings LINTED has no effect */
+ mknowarn = nowarn;
+ nowarn = 0;
+
+ for (sym = di->d_dlsyms; sym != NULL; sym = sym->s_dlnxt)
+ chkusg1(di->d_asm, sym);
+
+ nowarn = mknowarn;
+}
+
+/*
+ * Prints a warning for a single variable or label if it is not used or
+ * only set.
+ */
+void
+chkusg1(novar, sym)
+ int novar;
+ sym_t *sym;
+{
+ pos_t cpos;
+
+ if (sym->s_blklev == -1)
+ return;
+
+ STRUCT_ASSIGN(cpos, curr_pos);
+
+ if (sym->s_kind == FVFT) {
+ if (sym->s_arg) {
+ chkausg(novar, sym);
+ } else {
+ chkvusg(novar, sym);
+ }
+ } else if (sym->s_kind == FLAB) {
+ chklusg(sym);
+ } else if (sym->s_kind == FTAG) {
+ chktusg(sym);
+ }
+
+ STRUCT_ASSIGN(curr_pos, cpos);
+}
+
+static void
+chkausg(novar, arg)
+ int novar;
+ sym_t *arg;
+{
+ if (!arg->s_set)
+ lerror("chkausg() 1");
+
+ if (novar)
+ return;
+
+ if (!arg->s_used && vflag) {
+ STRUCT_ASSIGN(curr_pos, arg->s_dpos);
+ /* argument %s unused in function %s */
+ warning(231, arg->s_name, funcsym->s_name);
+ }
+}
+
+static void
+chkvusg(novar, sym)
+ int novar;
+ sym_t *sym;
+{
+ scl_t sc;
+ sym_t *xsym;
+
+ if (blklev == 0 || sym->s_blklev == 0)
+ lerror("chkvusg() 1");
+
+ /* errors in expressions easily cause lots of these warnings */
+ if (nerr != 0)
+ return;
+
+ /*
+ * XXX Only variables are checkd, although types should
+ * probably also be checked
+ */
+ if ((sc = sym->s_scl) != EXTERN && sc != STATIC &&
+ sc != AUTO && sc != REG) {
+ return;
+ }
+
+ if (novar)
+ return;
+
+ if (sc == EXTERN) {
+ if (!sym->s_used && !sym->s_set) {
+ STRUCT_ASSIGN(curr_pos, sym->s_dpos);
+ /* %s unused in function %s */
+ warning(192, sym->s_name, funcsym->s_name);
+ }
+ } else {
+ if (sym->s_set && !sym->s_used) {
+ STRUCT_ASSIGN(curr_pos, sym->s_spos);
+ /* %s set but not used in function %s */
+ warning(191, sym->s_name, funcsym->s_name);
+ } else if (!sym->s_used) {
+ STRUCT_ASSIGN(curr_pos, sym->s_dpos);
+ /* %s unused in function %s */
+ warning(192, sym->s_name, funcsym->s_name);
+ }
+ }
+
+ if (sc == EXTERN) {
+ /*
+ * information about usage is taken over into the symbol
+ * tabel entry at level 0 if the symbol was locally declared
+ * as an external symbol.
+ *
+ * XXX This is wrong for symbols declared static at level 0
+ * if the usage information stems from sizeof(). This is
+ * because symbols at level 0 only used in sizeof() are
+ * considered to not be used.
+ */
+ if ((xsym = sym->s_xsym) != NULL) {
+ if (sym->s_used && !xsym->s_used) {
+ xsym->s_used = 1;
+ STRUCT_ASSIGN(xsym->s_upos, sym->s_upos);
+ }
+ if (sym->s_set && !xsym->s_set) {
+ xsym->s_set = 1;
+ STRUCT_ASSIGN(xsym->s_spos, sym->s_spos);
+ }
+ }
+ }
+}
+
+static void
+chklusg(lab)
+ sym_t *lab;
+{
+ if (blklev != 1 || lab->s_blklev != 1)
+ lerror("chklusg() 1");
+
+ if (lab->s_set && !lab->s_used) {
+ STRUCT_ASSIGN(curr_pos, lab->s_spos);
+ /* label %s unused in function %s */
+ warning(192, lab->s_name, funcsym->s_name);
+ } else if (!lab->s_set) {
+ STRUCT_ASSIGN(curr_pos, lab->s_upos);
+ /* undefined label %s */
+ warning(23, lab->s_name);
+ }
+}
+
+static void
+chktusg(sym)
+ sym_t *sym;
+{
+ if (!incompl(sym->s_type))
+ return;
+
+ /* complain alwasy about incomplet tags declared inside blocks */
+ if (!zflag || dcs->d_ctx != EXTERN)
+ return;
+
+ STRUCT_ASSIGN(curr_pos, sym->s_dpos);
+ switch (sym->s_type->t_tspec) {
+ case STRUCT:
+ /* struct %s never defined */
+ warning(233, sym->s_name);
+ break;
+ case UNION:
+ /* union %s never defined */
+ warning(234, sym->s_name);
+ break;
+ case ENUM:
+ /* enum %s never defined */
+ warning(235, sym->s_name);
+ break;
+ default:
+ lerror("chktusg() 1");
+ }
+}
+
+/*
+ * Called after the entire translation unit has been parsed.
+ * Changes tentative definitions in definitions.
+ * Performs some tests on global Symbols. Detected Problems are:
+ * - defined variables of incomplete type
+ * - constant variables which are not initialized
+ * - static symbols which are never used
+ */
+void
+chkglsyms()
+{
+ sym_t *sym;
+ pos_t cpos;
+
+ if (blklev != 0 || dcs->d_nxt != NULL)
+ norecover();
+
+ STRUCT_ASSIGN(cpos, curr_pos);
+
+ for (sym = dcs->d_dlsyms; sym != NULL; sym = sym->s_dlnxt) {
+ if (sym->s_blklev == -1)
+ continue;
+ if (sym->s_kind == FVFT) {
+ chkglvar(sym);
+ } else if (sym->s_kind == FTAG) {
+ chktusg(sym);
+ } else {
+ if (sym->s_kind != FMOS)
+ lerror("chkglsyms() 1");
+ }
+ }
+
+ STRUCT_ASSIGN(curr_pos, cpos);
+}
+
+static void
+chkglvar(sym)
+ sym_t *sym;
+{
+ if (sym->s_scl == TYPEDEF || sym->s_scl == ENUMCON)
+ return;
+
+ if (sym->s_scl != EXTERN && sym->s_scl != STATIC)
+ lerror("chkglvar() 1");
+
+ glchksz(sym);
+
+ if (sym->s_scl == STATIC) {
+ if (sym->s_type->t_tspec == FUNC) {
+ if (sym->s_used && sym->s_def != DEF) {
+ STRUCT_ASSIGN(curr_pos, sym->s_upos);
+ /* static func. called but not def.. */
+ error(225, sym->s_name);
+ }
+ }
+ if (!sym->s_used) {
+ STRUCT_ASSIGN(curr_pos, sym->s_dpos);
+ if (sym->s_type->t_tspec == FUNC) {
+ if (sym->s_def == DEF) {
+ if (!sym->s_inline)
+ /* static function %s unused */
+ warning(236, sym->s_name);
+ } else {
+ /* static function %s decl. but ... */
+ warning(290, sym->s_name);
+ }
+ } else if (!sym->s_set) {
+ /* static variable %s unused */
+ warning(226, sym->s_name);
+ } else {
+ /* static variable %s set but not used */
+ warning(307, sym->s_name);
+ }
+ }
+ if (!tflag && sym->s_def == TDEF && sym->s_type->t_const) {
+ STRUCT_ASSIGN(curr_pos, sym->s_dpos);
+ /* const object %s should have initializer */
+ warning(227, sym->s_name);
+ }
+ }
+}
+
+static void
+glchksz(sym)
+ sym_t *sym;
+{
+ if (sym->s_def == TDEF) {
+ if (sym->s_type->t_tspec == FUNC)
+ /*
+ * this can happen if an syntax error occured
+ * after a function declaration
+ */
+ return;
+ STRUCT_ASSIGN(curr_pos, sym->s_dpos);
+ if (length(sym->s_type, sym->s_name) == 0 &&
+ sym->s_type->t_tspec == ARRAY && sym->s_type->t_dim == 0) {
+ /* empty array declaration: %s */
+ if (tflag || (sym->s_scl == EXTERN && !sflag)) {
+ warning(190, sym->s_name);
+ } else {
+ error(190, sym->s_name);
+ }
+ }
+ }
+}
+
+/*
+ * Prints information about location of previous definition/declaration.
+ */
+void
+prevdecl(msg, psym)
+ int msg;
+ sym_t *psym;
+{
+ pos_t cpos;
+
+ if (!rflag)
+ return;
+
+ STRUCT_ASSIGN(cpos, curr_pos);
+ STRUCT_ASSIGN(curr_pos, psym->s_dpos);
+ if (msg != -1) {
+ message(msg, psym->s_name);
+ } else if (psym->s_def == DEF || psym->s_def == TDEF) {
+ /* previous definition of %s */
+ message(261, psym->s_name);
+ } else {
+ /* previous declaration of %s */
+ message(260, psym->s_name);
+ }
+ STRUCT_ASSIGN(curr_pos, cpos);
+}
diff --git a/usr.bin/xlint/lint1/emit.c b/usr.bin/xlint/lint1/emit.c
new file mode 100644
index 0000000..f98f540
--- /dev/null
+++ b/usr.bin/xlint/lint1/emit.c
@@ -0,0 +1,241 @@
+/* $NetBSD: emit.c,v 1.2 1995/07/03 21:24:00 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: emit.c,v 1.2 1995/07/03 21:24:00 cgd Exp $";
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <err.h>
+
+#include "lint.h"
+
+/* name and handle of output file */
+static const char *loname;
+static FILE *lout;
+
+/* output buffer data */
+ob_t ob;
+
+static void outxbuf __P((void));
+
+
+/*
+ * initialize output
+ */
+void
+outopen(name)
+ const char *name;
+{
+ loname = name;
+
+ /* Ausgabedatei oeffnen */
+ if ((lout = fopen(name, "w")) == NULL)
+ err(1, "cannot open '%s'", name);
+
+ /* Ausgabepuffer anlegen */
+ ob.o_len = 1024;
+ ob.o_end = (ob.o_buf = ob.o_nxt = xmalloc(ob.o_len)) + ob.o_len;
+}
+
+/*
+ * flush output buffer and close file
+ */
+void
+outclose()
+{
+ outclr();
+ if (fclose(lout) == EOF)
+ err(1, "cannot close '%s'", loname);
+}
+
+/*
+ * resize output buffer
+ */
+static void
+outxbuf()
+{
+ ptrdiff_t coffs;
+
+ coffs = ob.o_nxt - ob.o_buf;
+ ob.o_len *= 2;
+ ob.o_end = (ob.o_buf = xrealloc(ob.o_buf, ob.o_len)) + ob.o_len;
+ ob.o_nxt = ob.o_buf + coffs;
+}
+
+/*
+ * reset output buffer
+ * if it is not empty, it is flushed
+ */
+void
+outclr()
+{
+ size_t sz;
+
+ if (ob.o_buf != ob.o_nxt) {
+ outchar('\n');
+ sz = ob.o_nxt - ob.o_buf;
+ if (sz > ob.o_len)
+ errx(1, "internal error: outclr() 1");
+ if (fwrite(ob.o_buf, sz, 1, lout) != 1)
+ err(1, "cannot write to %s", loname);
+ ob.o_nxt = ob.o_buf;
+ }
+}
+
+/*
+ * write a character to the output buffer
+ */
+void
+outchar(c)
+ int c;
+{
+ if (ob.o_nxt == ob.o_end)
+ outxbuf();
+ *ob.o_nxt++ = (char)c;
+}
+
+/*
+ * write a character to the output buffer, qouted if necessary
+ */
+void
+outqchar(c)
+ int c;
+{
+ if (isprint(c) && c != '\\' && c != '"' && c != '\'') {
+ outchar(c);
+ } else {
+ outchar('\\');
+ switch (c) {
+ case '\\':
+ outchar('\\');
+ break;
+ case '"':
+ outchar('"');
+ break;
+ case '\'':
+ outchar('\'');
+ break;
+ case '\b':
+ outchar('b');
+ break;
+ case '\t':
+ outchar('t');
+ break;
+ case '\n':
+ outchar('n');
+ break;
+ case '\f':
+ outchar('f');
+ break;
+ case '\r':
+ outchar('r');
+ break;
+#ifdef __STDC__
+ case '\v':
+#else
+ case '\013':
+#endif
+ outchar('v');
+ break;
+#ifdef __STDC__
+ case '\a':
+#else
+ case '\007':
+#endif
+ outchar('a');
+ break;
+ default:
+ outchar((((u_int)c >> 6) & 07) + '0');
+ outchar((((u_int)c >> 3) & 07) + '0');
+ outchar((c & 07) + '0');
+ break;
+ }
+ }
+}
+
+/*
+ * write a strint to the output buffer
+ * the string must not contain any characters which
+ * should be quoted
+ */
+void
+outstrg(s)
+ const char *s;
+{
+ while (*s != '\0') {
+ if (ob.o_nxt == ob.o_end)
+ outxbuf();
+ *ob.o_nxt++ = *s++;
+ }
+}
+
+/*
+ * write an integer value to toe output buffer
+ */
+void
+outint(i)
+ int i;
+{
+ if ((ob.o_end - ob.o_nxt) < 3 * sizeof (int))
+ outxbuf();
+ ob.o_nxt += sprintf(ob.o_nxt, "%d", i);
+}
+
+/*
+ * write the name of a symbol to the output buffer
+ * the name is preceeded by its length
+ */
+void
+outname(name)
+ const char *name;
+{
+ if (name == NULL)
+ errx(1, "internal error: outname() 1");
+ outint((int)strlen(name));
+ outstrg(name);
+}
+
+/*
+ * write the name of the .c source
+ */
+void
+outsrc(name)
+ const char *name;
+{
+ outclr();
+ outchar('S');
+ outstrg(name);
+}
diff --git a/usr.bin/xlint/lint1/emit1.c b/usr.bin/xlint/lint1/emit1.c
new file mode 100644
index 0000000..ee2434e
--- /dev/null
+++ b/usr.bin/xlint/lint1/emit1.c
@@ -0,0 +1,587 @@
+/* $NetBSD: emit1.c,v 1.4 1995/10/02 17:21:28 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: emit1.c,v 1.4 1995/10/02 17:21:28 jpo Exp $";
+#endif
+
+#include <ctype.h>
+
+#include "lint1.h"
+
+static void outtt __P((sym_t *, sym_t *));
+static void outfstrg __P((strg_t *));
+
+/*
+ * Write type into the output buffer.
+ * The type is written as a sequence of substrings, each of which describes a
+ * node of type type_t
+ * a node is coded as follows:
+ * char C
+ * signed char s C
+ * unsigned char u C
+ * short S
+ * unsigned short u S
+ * int I
+ * unsigned int u I
+ * long L
+ * unsigned long u L
+ * long long Q
+ * unsigned long long u Q
+ * float s D
+ * double D
+ * long double l D
+ * void V
+ * * P
+ * [n] A n
+ * () F
+ * (void) F 0
+ * (n arguments) F n arg1 arg2 ... argn
+ * (n arguments, ...) F n arg1 arg2 ... argn-1 E
+ * (a, b, c, ...) f n arg1 arg2 ...
+ * enum tag e T tag_or_typename
+ * struct tag s T tag_or_typename
+ * union tag u T tag_or_typename
+ *
+ * tag_or_typename 0 no tag or type name
+ * 1 n tag Tag
+ * 2 n typename only type name
+ *
+ * spaces are only for better readability
+ * additionaly it is possible to prepend the characters 'c' (for const)
+ * and 'v' (for volatile)
+ */
+void
+outtype(tp)
+ type_t *tp;
+{
+ int t, s, na;
+ sym_t *arg;
+ tspec_t ts;
+
+ while (tp != NULL) {
+ if ((ts = tp->t_tspec) == INT && tp->t_isenum)
+ ts = ENUM;
+ switch (ts) {
+ case CHAR: t = 'C'; s = '\0'; break;
+ case SCHAR: t = 'C'; s = 's'; break;
+ case UCHAR: t = 'C'; s = 'u'; break;
+ case SHORT: t = 'S'; s = '\0'; break;
+ case USHORT: t = 'S'; s = 'u'; break;
+ case INT: t = 'I'; s = '\0'; break;
+ case UINT: t = 'I'; s = 'u'; break;
+ case LONG: t = 'L'; s = '\0'; break;
+ case ULONG: t = 'L'; s = 'u'; break;
+ case QUAD: t = 'Q'; s = '\0'; break;
+ case UQUAD: t = 'Q'; s = 'u'; break;
+ case FLOAT: t = 'D'; s = 's'; break;
+ case DOUBLE: t = 'D'; s = '\0'; break;
+ case LDOUBLE: t = 'D'; s = 'l'; break;
+ case VOID: t = 'V'; s = '\0'; break;
+ case PTR: t = 'P'; s = '\0'; break;
+ case ARRAY: t = 'A'; s = '\0'; break;
+ case FUNC: t = 'F'; s = '\0'; break;
+ case ENUM: t = 'T'; s = 'e'; break;
+ case STRUCT: t = 'T'; s = 's'; break;
+ case UNION: t = 'T'; s = 'u'; break;
+ default:
+ lerror("outtyp() 1");
+ }
+ if (tp->t_const)
+ outchar('c');
+ if (tp->t_volatile)
+ outchar('v');
+ if (s != '\0')
+ outchar(s);
+ outchar(t);
+ if (ts == ARRAY) {
+ outint(tp->t_dim);
+ } else if (ts == ENUM) {
+ outtt(tp->t_enum->etag, tp->t_enum->etdef);
+ } else if (ts == STRUCT || ts == UNION) {
+ outtt(tp->t_str->stag, tp->t_str->stdef);
+ } else if (ts == FUNC && tp->t_proto) {
+ na = 0;
+ for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt)
+ na++;
+ if (tp->t_vararg)
+ na++;
+ outint(na);
+ for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt)
+ outtype(arg->s_type);
+ if (tp->t_vararg)
+ outchar('E');
+ }
+ tp = tp->t_subt;
+ }
+}
+
+/*
+ * type to string
+ * used for debugging output
+ *
+ * it uses its own output buffer for conversion
+ */
+const char *
+ttos(tp)
+ type_t *tp;
+{
+ static ob_t tob;
+ ob_t tmp;
+
+ if (tob.o_buf == NULL) {
+ tob.o_len = 64;
+ tob.o_buf = tob.o_nxt = xmalloc(tob.o_len);
+ tob.o_end = tob.o_buf + tob.o_len;
+ }
+
+ tmp = ob;
+ ob = tob;
+ ob.o_nxt = ob.o_buf;
+ outtype(tp);
+ outchar('\0');
+ tob = ob;
+ ob = tmp;
+
+ return (tob.o_buf);
+}
+
+/*
+ * write the name of a tag or typename
+ *
+ * if the tag is named, the name of the
+ * tag is written, otherwise, if a typename exists which
+ * refers to this tag, this typename is written
+ */
+static void
+outtt(tag, tdef)
+ sym_t *tag, *tdef;
+{
+ if (tag->s_name != unnamed) {
+ outint(1);
+ outname(tag->s_name);
+ } else if (tdef != NULL) {
+ outint(2);
+ outname(tdef->s_name);
+ } else {
+ outint(0);
+ }
+}
+
+/*
+ * write information about an global declared/defined symbol
+ * with storage class extern
+ *
+ * informations about function definitions are written in outfdef(),
+ * not here
+ */
+void
+outsym(sym, sc, def)
+ sym_t *sym;
+ scl_t sc;
+ def_t def;
+{
+ /*
+ * Static function declarations must also be written to the output
+ * file. Compatibility of function declarations (for both static
+ * and extern functions) must be checked in lint2. Lint1 can't do
+ * this, especially not, if functions are declared at block level
+ * before their first declaration at level 0.
+ */
+ if (sc != EXTERN && !(sc == STATIC && sym->s_type->t_tspec == FUNC))
+ return;
+
+ /* reset buffer */
+ outclr();
+
+ /*
+ * line number of .c source, 'd' for declaration, Id of current
+ * source (.c or .h), and line in current source.
+ */
+ outint(csrc_pos.p_line);
+ outchar('d');
+ outint(getfnid(sym->s_dpos.p_file));
+ outchar('.');
+ outint(sym->s_dpos.p_line);
+
+ /* flags */
+
+ switch (def) {
+ case DEF:
+ /* defined */
+ outchar('d');
+ break;
+ case TDEF:
+ /* tentative defined */
+ outchar('t');
+ break;
+ case DECL:
+ /* declared */
+ outchar('e');
+ break;
+ default:
+ lerror("outsym() 2");
+ }
+ if (llibflg && def != DECL) {
+ /*
+ * mark it as used so we get no warnings from lint2 about
+ * unused symbols in libraries.
+ */
+ outchar('u');
+ }
+
+ if (sc == STATIC)
+ outchar('s');
+
+ /* name of the symbol */
+ outname(sym->s_name);
+
+ /* type of the symbol */
+ outtype(sym->s_type);
+}
+
+/*
+ * write information about function definition
+ *
+ * this is also done for static functions so we are able to check if
+ * they are called with proper argument types
+ */
+void
+outfdef(fsym, posp, rval, osdef, args)
+ sym_t *fsym, *args;
+ pos_t *posp;
+ int rval, osdef;
+{
+ int narg;
+ sym_t *arg;
+
+ /* reset the buffer */
+ outclr();
+
+ /*
+ * line number of .c source, 'd' for declaration, Id of current
+ * source (.c or .h), and line in current source
+ *
+ * we are already at the end of the function. If we are in the
+ * .c source, posp->p_line is correct, otherwise csrc_pos.p_line
+ * (for functions defined in header files).
+ */
+ if (posp->p_file == csrc_pos.p_file) {
+ outint(posp->p_line);
+ } else {
+ outint(csrc_pos.p_line);
+ }
+ outchar('d');
+ outint(getfnid(posp->p_file));
+ outchar('.');
+ outint(posp->p_line);
+
+ /* flags */
+
+ /* both SCANFLIKE and PRINTFLIKE imply VARARGS */
+ if (prflstrg != -1) {
+ nvararg = prflstrg;
+ } else if (scflstrg != -1) {
+ nvararg = scflstrg;
+ }
+
+ if (nvararg != -1) {
+ outchar('v');
+ outint(nvararg);
+ }
+ if (scflstrg != -1) {
+ outchar('S');
+ outint(scflstrg);
+ }
+ if (prflstrg != -1) {
+ outchar('P');
+ outint(prflstrg);
+ }
+ nvararg = prflstrg = scflstrg = -1;
+
+ outchar('d');
+
+ if (rval)
+ /* has return value */
+ outchar('r');
+
+ if (llibflg)
+ /*
+ * mark it as used so lint2 does not complain about
+ * unused symbols in libraries
+ */
+ outchar('u');
+
+ if (osdef)
+ /* old style function definition */
+ outchar('o');
+
+ if (fsym->s_scl == STATIC)
+ outchar('s');
+
+ /* name of function */
+ outname(fsym->s_name);
+
+ /* argument types and return value */
+ if (osdef) {
+ narg = 0;
+ for (arg = args; arg != NULL; arg = arg->s_nxt)
+ narg++;
+ outchar('f');
+ outint(narg);
+ for (arg = args; arg != NULL; arg = arg->s_nxt)
+ outtype(arg->s_type);
+ outtype(fsym->s_type->t_subt);
+ } else {
+ outtype(fsym->s_type);
+ }
+}
+
+/*
+ * write out all information necessary for lint2 to check function
+ * calls
+ *
+ * rvused is set if the return value is used (asigned to a variable)
+ * rvdisc is set if the return value is not used and not ignored
+ * (casted to void)
+ */
+void
+outcall(tn, rvused, rvdisc)
+ tnode_t *tn;
+ int rvused, rvdisc;
+{
+ tnode_t *args, *arg;
+ int narg, n, i;
+ quad_t q;
+ tspec_t t;
+
+ /* reset buffer */
+ outclr();
+
+ /*
+ * line number of .c source, 'c' for function call, Id of current
+ * source (.c or .h), and line in current source
+ */
+ outint(csrc_pos.p_line);
+ outchar('c');
+ outint(getfnid(curr_pos.p_file));
+ outchar('.');
+ outint(curr_pos.p_line);
+
+ /*
+ * flags; 'u' and 'i' must be last to make sure a letter
+ * is between the numeric argument of a flag and the name of
+ * the function
+ */
+ narg = 0;
+ args = tn->tn_right;
+ for (arg = args; arg != NULL; arg = arg->tn_right)
+ narg++;
+ /* informations about arguments */
+ for (n = 1; n <= narg; n++) {
+ /* the last argument is the top one in the tree */
+ for (i = narg, arg = args; i > n; i--, arg = arg->tn_right) ;
+ arg = arg->tn_left;
+ if (arg->tn_op == CON) {
+ if (isityp(t = arg->tn_type->t_tspec)) {
+ /*
+ * XXX it would probably be better to
+ * explizitly test the sign
+ */
+ if ((q = arg->tn_val->v_quad) == 0) {
+ /* zero constant */
+ outchar('z');
+ } else if (msb(q, t, 0) == 0) {
+ /* positive if casted to signed */
+ outchar('p');
+ } else {
+ /* negative if casted to signed */
+ outchar('n');
+ }
+ outint(n);
+ }
+ } else if (arg->tn_op == AMPER &&
+ arg->tn_left->tn_op == STRING &&
+ arg->tn_left->tn_strg->st_tspec == CHAR) {
+ /* constant string, write all format specifiers */
+ outchar('s');
+ outint(n);
+ outfstrg(arg->tn_left->tn_strg);
+ }
+
+ }
+ /* return value discarded/used/ignored */
+ outchar(rvdisc ? 'd' : (rvused ? 'u' : 'i'));
+
+ /* name of the called function */
+ outname(tn->tn_left->tn_left->tn_sym->s_name);
+
+ /* types of arguments */
+ outchar('f');
+ outint(narg);
+ for (n = 1; n <= narg; n++) {
+ /* the last argument is the top one in the tree */
+ for (i = narg, arg = args; i > n; i--, arg = arg->tn_right) ;
+ outtype(arg->tn_left->tn_type);
+ }
+ /* expected type of return value */
+ outtype(tn->tn_type);
+}
+
+/*
+ * extracts potential format specifiers for printf() and scanf() and
+ * writes them, enclosed in "" and qouted if necessary, to the output buffer
+ */
+static void
+outfstrg(strg)
+ strg_t *strg;
+{
+ int c, oc, first;
+ u_char *cp;
+
+ if (strg->st_tspec != CHAR)
+ lerror("outfstrg() 1");
+
+ cp = strg->st_cp;
+
+ outchar('"');
+
+ c = *cp++;
+
+ while (c != '\0') {
+
+ if (c != '%') {
+ c = *cp++;
+ continue;
+ }
+
+ outqchar('%');
+ c = *cp++;
+
+ /* flags for printf and scanf and *-fieldwidth for printf */
+ while (c != '\0' && (c == '-' || c == '+' || c == ' ' ||
+ c == '#' || c == '0' || c == '*')) {
+ outqchar(c);
+ c = *cp++;
+ }
+
+ /* numeric field width */
+ while (c != '\0' && isdigit(c)) {
+ outqchar(c);
+ c = *cp++;
+ }
+
+ /* precision for printf */
+ if (c == '.') {
+ outqchar(c);
+ if ((c = *cp++) == '*') {
+ outqchar(c);
+ c = *cp++;
+ } else {
+ while (c != '\0' && isdigit(c)) {
+ outqchar(c);
+ c = *cp++;
+ }
+ }
+ }
+
+ /* h, l, L and q flags fpr printf and scanf */
+ if (c == 'h' || c == 'l' || c == 'L' || c == 'q') {
+ outqchar(c);
+ c = *cp++;
+ }
+
+ /*
+ * The last character. It is always written so we can detect
+ * invalid format specifiers.
+ */
+ if (c != '\0') {
+ outqchar(c);
+ oc = c;
+ c = *cp++;
+ /*
+ * handle [ for scanf. [-] means that a minus sign
+ * was found at an undefined position.
+ */
+ if (oc == '[') {
+ if (c == '^')
+ c = *cp++;
+ if (c == ']')
+ c = *cp++;
+ first = 1;
+ while (c != '\0' && c != ']') {
+ if (c == '-') {
+ if (!first && *cp != ']')
+ outqchar(c);
+ }
+ first = 0;
+ c = *cp++;
+ }
+ if (c == ']') {
+ outqchar(c);
+ c = *cp++;
+ }
+ }
+ }
+
+ }
+
+ outchar('"');
+}
+
+/*
+ * writes a record if sym was used
+ */
+void
+outusg(sym)
+ sym_t *sym;
+{
+ /* reset buffer */
+ outclr();
+
+ /*
+ * line number of .c source, 'u' for used, Id of current
+ * source (.c or .h), and line in current source
+ */
+ outint(csrc_pos.p_line);
+ outchar('u');
+ outint(getfnid(curr_pos.p_file));
+ outchar('.');
+ outint(curr_pos.p_line);
+
+ /* necessary to delimit both numbers */
+ outchar('x');
+
+ /* Den Namen des Symbols ausgeben */
+ outname(sym->s_name);
+}
diff --git a/usr.bin/xlint/lint1/err.c b/usr.bin/xlint/lint1/err.c
new file mode 100644
index 0000000..c2be198
--- /dev/null
+++ b/usr.bin/xlint/lint1/err.c
@@ -0,0 +1,543 @@
+/* $NetBSD: err.c,v 1.8 1995/10/02 17:37:00 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: err.c,v 1.8 1995/10/02 17:37:00 jpo Exp $";
+#endif
+
+/* number of errors found */
+int nerr;
+
+/* number of syntax errors */
+int sytxerr;
+
+#include <stdlib.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "lint1.h"
+
+static const char *basename __P((const char *));
+static void verror __P((int, va_list));
+static void vwarning __P((int, va_list));
+
+
+const char *msgs[] = {
+ "syntax error: empty declaration", /* 0 */
+ "old style declaration; add int", /* 1 */
+ "empty declaration", /* 2 */
+ "%s declared in argument declaration list", /* 3 */
+ "illegal type combination", /* 4 */
+ "modifying typedef with '%s'; only qualifiers allowed", /* 5 */
+ "use 'double' instead of 'long float'", /* 6 */
+ "only one storage class allowed", /* 7 */
+ "illegal storage class", /* 8 */
+ "only register valid as formal parameter storage class", /* 9 */
+ "duplicate '%s'", /* 10 */
+ "bit-field initializer out of range", /* 11 */
+ "compiler takes size of function", /* 12 */
+ "incomplete enum type: %s", /* 13 */
+ "compiler takes alignment of function", /* 14 */
+ "function returns illegal type", /* 15 */
+ "array of function is illegal", /* 16 */
+ "null dimension", /* 17 */
+ "illegal use of 'void'", /* 18 */
+ "void type for %s", /* 19 */
+ "zero or negative array dimension", /* 20 */
+ "redeclaration of formal parameter %s", /* 21 */
+ "incomplete or misplaced function definition", /* 22 */
+ "undefined label %s", /* 23 */
+ "cannot initialize function: %s", /* 24 */
+ "cannot initialize typedef: %s", /* 25 */
+ "cannot initialize extern declaration: %s", /* 26 */
+ "redeclaration of %s", /* 27 */
+ "redefinition of %s", /* 28 */
+ "previously declared extern, becomes static: %s", /* 29 */
+ "redeclaration of %s; ANSI C requires static", /* 30 */
+ "incomplete structure or union %s: %s", /* 31 */
+ "argument type defaults to 'int': %s", /* 32 */
+ "duplicate member name: %s", /* 33 */
+ "nonportable bit-field type", /* 34 */
+ "illegal bit-field type", /* 35 */
+ "illegal bit-field size", /* 36 */
+ "zero size bit-field", /* 37 */
+ "function illegal in structure or union", /* 38 */
+ "illegal zero sized structure member: %s", /* 39 */
+ "unknown size: %s", /* 40 */
+ "illegal use of bit-field", /* 41 */
+ "forward reference to enum type", /* 42 */
+ "redefinition hides earlier one: %s", /* 43 */
+ "declaration introduces new type in ANSI C: %s %s", /* 44 */
+ "base type is really '%s %s'", /* 45 */
+ "(%s) tag redeclared", /* 46 */
+ "zero sized %s", /* 47 */
+ "overflow in enumeration values: %s", /* 48 */
+ "struct or union member must be named", /* 49 */
+ "a function is declared as an argument: %s", /* 50 */
+ "parameter mismatch: %d declared, %d defined", /* 51 */
+ "cannot initialize parameter: %s", /* 52 */
+ "declared argument %s is missing", /* 53 */
+ "trailing ',' prohibited in enum declaration", /* 54 */
+ "integral constant expression expected", /* 55 */
+ "integral constant too large", /* 56 */
+ "enumeration constant hides parameter: %s", /* 57 */
+ "type does not match prototype: %s", /* 58 */
+ "formal parameter lacks name: param #%d", /* 59 */
+ "void must be sole parameter", /* 60 */
+ "void parameter cannot have name: %s", /* 61 */
+ "function prototype parameters must have types", /* 62 */
+ "prototype does not match old-style definition", /* 63 */
+ "()-less function definition", /* 64 */
+ "%s has no named members", /* 65 */
+ "syntax requires ';' after last struct/union member", /* 66 */
+ "cannot return incomplete type", /* 67 */
+ "typedef already qualified with '%s'", /* 68 */
+ "inappropriate qualifiers with 'void'", /* 69 */
+ "%soperand of '%s' is unsigned in ANSI C", /* 70 */
+ "too many characters in character constant", /* 71 */
+ "typedef declares no type name", /* 72 */
+ "empty character constant", /* 73 */
+ "no hex digits follow \\x", /* 74 */
+ "overflow in hex escape", /* 75 */
+ "character escape does not fit in character", /* 76 */
+ "bad octal digit %c", /* 77 */
+ "nonportable character escape", /* 78 */
+ "dubious escape \\%c", /* 79 */
+ "dubious escape \\%o", /* 80 */
+ "\\a undefined in traditional C", /* 81 */
+ "\\x undefined in traditional C", /* 82 */
+ "storage class after type is obsolescent", /* 83 */
+ "ANSI C requires formal parameter before '...'", /* 84 */
+ "dubious tag declaration: %s %s", /* 85 */
+ "automatic hides external declaration: %s", /* 86 */
+ "static hides external declaration: %s", /* 87 */
+ "typedef hides external declaration: %s", /* 88 */
+ "typedef redeclared: %s", /* 89 */
+ "inconsistent redeclaration of extern: %s", /* 90 */
+ "declaration hides parameter: %s", /* 91 */
+ "inconsistent redeclaration of static: %s", /* 92 */
+ "dubious static function at block level: %s", /* 93 */
+ "function has illegal storage class: %s", /* 94 */
+ "declaration hides earlier one: %s", /* 95 */
+ "cannot dereference non-pointer type", /* 96 */
+ "suffix U is illegal in traditional C", /* 97 */
+ "suffixes F and L are illegal in traditional C", /* 98 */
+ "%s undefined", /* 99 */
+ "unary + is illegal in traditional C", /* 100 */
+ "undefined struct/union member: %s", /* 101 */
+ "illegal member use: %s", /* 102 */
+ "left operand of '.' must be struct/union object", /* 103 */
+ "left operand of '->' must be pointer to struct/union", /* 104 */
+ "non-unique member requires struct/union %s", /* 105 */
+ "left operand of '->' must be pointer", /* 106 */
+ "operands of '%s' have incompatible types", /* 107 */
+ "operand of '%s' has incompatible type", /* 108 */
+ "void type illegal in expression", /* 109 */
+ "pointer to function is not allowed here", /* 110 */
+ "unacceptable operand of '%s'", /* 111 */
+ "cannot take address of bit-field", /* 112 */
+ "cannot take address of register %s", /* 113 */
+ "%soperand of '%s' must be lvalue", /* 114 */
+ "%soperand of '%s' must be modifiable lvalue", /* 115 */
+ "illegal pointer subtraction", /* 116 */
+ "bitwise operation on signed value possibly nonportable", /* 117 */
+ "semantics of '%s' change in ANSI C; use explicit cast", /* 118 */
+ "conversion of '%s' to '%s' is out of range", /* 119 */
+ "bitwise operation on signed value nonportable", /* 120 */
+ "negative shift", /* 121 */
+ "shift greater than size of object", /* 122 */
+ "illegal combination of pointer and integer, op %s", /* 123 */
+ "illegal pointer combination, op %s", /* 124 */
+ "ANSI C forbids ordered comparisons of pointers to functions",/* 125 */
+ "incompatible types in conditional", /* 126 */
+ "'&' before array or function: ignored", /* 127 */
+ "operands have incompatible pointer types, op %s", /* 128 */
+ "expression has null effect", /* 129 */
+ "enum type mismatch, op %s", /* 130 */
+ "conversion to '%s' may sign-extend incorrectly", /* 131 */
+ "conversion from '%s' may lose accuracy", /* 132 */
+ "conversion of pointer to '%s' loses bits", /* 133 */
+ "conversion of pointer to '%s' may lose bits", /* 134 */
+ "possible pointer alignment problem", /* 135 */
+ "cannot do pointer arithmetic on operand of unknown size", /* 136 */
+ "use of incomplete enum type, op %s", /* 137 */
+ "unknown operand size, op %s", /* 138 */
+ "division by 0", /* 139 */
+ "modulus by 0", /* 140 */
+ "integer overflow detected, op %s", /* 141 */
+ "floating point overflow detected, op %s", /* 142 */
+ "cannot take size of incomplete type", /* 143 */
+ "cannot take size of function", /* 144 */
+ "cannot take size of bit-field", /* 145 */
+ "cannot take size of void", /* 146 */
+ "invalid cast expression", /* 147 */
+ "improper cast of void expression", /* 148 */
+ "illegal function", /* 149 */
+ "argument mismatch: %d arg%s passed, %d expected", /* 150 */
+ "void expressions may not be arguments, arg #%d", /* 151 */
+ "argument cannot have unknown size, arg #%d", /* 152 */
+ "argument has incompatible pointer type, arg #%d", /* 153 */
+ "illegal combination of pointer and integer, arg #%d", /* 154 */
+ "argument is incompatible with prototype, arg #%d", /* 155 */
+ "enum type mismatch, arg #%d", /* 156 */
+ "ANSI C treats constant as unsigned", /* 157 */
+ "%s may be used before set", /* 158 */
+ "assignment in conditional context", /* 159 */
+ "operator '==' found where '=' was expected", /* 160 */
+ "constant in conditional context", /* 161 */
+ "comparision of %s with %s, op %s", /* 162 */
+ "a cast does not yield an lvalue", /* 163 */
+ "assignment of negative constant to unsigned type", /* 164 */
+ "constant truncated by assignment", /* 165 */
+ "precision lost in bit-field assignment", /* 166 */
+ "array subscript cannot be negative: %ld", /* 167 */
+ "array subscript cannot be > %d: %ld", /* 168 */
+ "precedence confusion possible: parenthesize!", /* 169 */
+ "first operand must have scalar type, op ? :", /* 170 */
+ "assignment type mismatch", /* 171 */
+ "too many struct/union initializers", /* 172 */
+ "too many array initializers", /* 173 */
+ "too many initializers", /* 174 */
+ "initialisation of an incomplete type", /* 175 */
+ "invalid initializer", /* 176 */
+ "non-constant initializer", /* 177 */
+ "initializer does not fit", /* 178 */
+ "cannot initialize struct/union with no named member", /* 179 */
+ "bit-field initializer does not fit", /* 180 */
+ "{}-enclosed initializer required", /* 181 */
+ "incompatible pointer types", /* 182 */
+ "illegal combination of pointer and integer", /* 183 */
+ "illegal pointer combination", /* 184 */
+ "initialisation type mismatch", /* 185 */
+ "bit-field initialisation is illegal in traditional C", /* 186 */
+ "non-null byte ignored in string initializer", /* 187 */
+ "no automatic aggregate initialization in traditional C", /* 188 */
+ "assignment of struct/union illegal in traditional C", /* 189 */
+ "empty array declaration: %s", /* 190 */
+ "%s set but not used in function %s", /* 191 */
+ "%s unused in function %s", /* 192 */
+ "statement not reached", /* 193 */
+ "label %s redefined", /* 194 */
+ "case not in switch", /* 195 */
+ "case label affected by conversion", /* 196 */
+ "non-constant case expression", /* 197 */
+ "non-integral case expression", /* 198 */
+ "duplicate case in switch: %ld", /* 199 */
+ "duplicate case in switch: %lu", /* 200 */
+ "default outside switch", /* 201 */
+ "duplicate default in switch", /* 202 */
+ "case label must be of type `int' in traditional C", /* 203 */
+ "controlling expressions must have scalar type", /* 204 */
+ "switch expression must have integral type", /* 205 */
+ "enumeration value(s) not handled in switch", /* 206 */
+ "loop not entered at top", /* 207 */
+ "break outside loop or switch", /* 208 */
+ "continue outside loop", /* 209 */
+ "enum type mismatch in initialisation", /* 210 */
+ "return value type mismatch", /* 211 */
+ "cannot return incomplete type", /* 212 */
+ "void function %s cannot return value", /* 213 */
+ "function %s expects to return value", /* 214 */
+ "function implicitly declared to return int", /* 215 */
+ "function %s has return (e); and return;", /* 216 */
+ "function %s falls off bottom without returning value", /* 217 */
+ "ANSI C treats constant as unsigned, op %s", /* 218 */
+ "concatenated strings are illegal in traditional C", /* 219 */
+ "fallthrough on case statement", /* 220 */
+ "initialisation of unsigned with negative constant", /* 221 */
+ "conversion of negative constant to unsigned type", /* 222 */
+ "end-of-loop code not reached", /* 223 */
+ "cannot recover from previous errors", /* 224 */
+ "static function called but not defined: %s()", /* 225 */
+ "static variable %s unused", /* 226 */
+ "const object %s should have initializer", /* 227 */
+ "function cannot return const or volatile object", /* 228 */
+ "questionable conversion of function pointer", /* 229 */
+ "nonportable character comparision, op %s", /* 230 */
+ "argument %s unused in function %s", /* 231 */
+ "label %s unused in function %s", /* 232 */
+ "struct %s never defined", /* 233 */
+ "union %s never defined", /* 234 */
+ "enum %s never defined", /* 235 */
+ "static function %s unused", /* 236 */
+ "redeclaration of formal parameter %s", /* 237 */
+ "initialisation of union is illegal in traditional C", /* 238 */
+ "constant argument to NOT", /* 239 */
+ "assignment of different structures", /* 240 */
+ "dubious operation on enum, op %s", /* 241 */
+ "combination of '%s' and '%s', op %s", /* 242 */
+ "dubious comparision of enums, op %s", /* 243 */
+ "illegal structure pointer combination", /* 244 */
+ "illegal structure pointer combination, op %s", /* 245 */
+ "dubious conversion of enum to '%s'", /* 246 */
+ "pointer casts may be troublesome", /* 247 */
+ "floating-point constant out of range", /* 248 */
+ "syntax error", /* 249 */
+ "unknown character \\%o", /* 250 */
+ "malformed integer constant", /* 251 */
+ "integer constant out of range", /* 252 */
+ "unterminated character constant", /* 253 */
+ "newline in string or char constant", /* 254 */
+ "undefined or invalid # directive", /* 255 */
+ "unterminated comment", /* 256 */
+ "extra characters in lint comment", /* 257 */
+ "unterminated string constant", /* 258 */
+ "conversion to '%s' due to prototype, arg #%d", /* 259 */
+ "previous declaration of %s", /* 260 */
+ "previous definition of %s", /* 261 */
+ "\\\" inside character constants undefined in traditional C", /* 262 */
+ "\\? undefined in traditional C", /* 263 */
+ "\\v undefined in traditional C", /* 264 */
+ "%s C does not support 'long long'", /* 265 */
+ "'long double' is illegal in traditional C", /* 266 */
+ "shift equal to size of object", /* 267 */
+ "variable declared inline: %s", /* 268 */
+ "argument declared inline: %s", /* 269 */
+ "function prototypes are illegal in traditional C", /* 270 */
+ "switch expression must be of type `int' in traditional C", /* 271 */
+ "empty translation unit", /* 272 */
+ "bit-field type '%s' invalid in ANSI C", /* 273 */
+ "ANSI C forbids comparision of %s with %s", /* 274 */
+ "cast discards 'const' from pointer target type", /* 275 */
+ "", /* 276 */
+ "initialisation of '%s' with '%s'", /* 277 */
+ "combination of '%s' and '%s', arg #%d", /* 278 */
+ "combination of '%s' and '%s' in return", /* 279 */
+ "must be outside function: /* %s */", /* 280 */
+ "duplicate use of /* %s */", /* 281 */
+ "must precede function definition: /* %s */", /* 282 */
+ "argument number mismatch with directive: /* %s */", /* 283 */
+ "fallthrough on default statement", /* 284 */
+ "prototype declaration", /* 285 */
+ "function definition is not a prototype", /* 286 */
+ "function declaration is not a prototype", /* 287 */
+ "dubious use of /* VARARGS */ with /* %s */", /* 288 */
+ "can't be used together: /* PRINTFLIKE */ /* SCANFLIKE */", /* 289 */
+ "static function %s declared but not defined", /* 290 */
+ "invalid multibyte character", /* 291 */
+ "cannot concatenate wide and regular string literals", /* 292 */
+ "argument %d must be 'char *' for PRINTFLIKE/SCANFLIKE", /* 293 */
+ "multi-character character constant", /* 294 */
+ "conversion of '%s' to '%s' is out of range, arg #%d", /* 295 */
+ "conversion of negative constant to unsigned type, arg #%d", /* 296 */
+ "conversion to '%s' may sign-extend incorrectly, arg #%d", /* 297 */
+ "conversion from '%s' may lose accuracy, arg #%d", /* 298 */
+ "prototype does not match old style definition, arg #%d", /* 299 */
+ "old style definition", /* 300 */
+ "array of incomplete type", /* 301 */
+ "%s returns pointer to automatic object", /* 302 */
+ "ANSI C forbids conversion of %s to %s", /* 303 */
+ "ANSI C forbids conversion of %s to %s, arg #%d", /* 304 */
+ "ANSI C forbids conversion of %s to %s, op %s", /* 305 */
+ "constant truncated by conversion, op %s", /* 306 */
+ "static variable %s set but not used", /* 307 */
+ "", /* 308 */
+ "extra bits set to 0 in conversion of '%s' to '%s', op %s", /* 309 */
+};
+
+/*
+ * If Fflag is not set basename() returns a pointer to the last
+ * component of the path, otherwise it returns the argument.
+ */
+static const char *
+basename(path)
+ const char *path;
+{
+ const char *cp, *cp1, *cp2;
+
+ if (Fflag)
+ return (path);
+
+ cp = cp1 = cp2 = path;
+ while (*cp != '\0') {
+ if (*cp++ == '/') {
+ cp2 = cp1;
+ cp1 = cp;
+ }
+ }
+ return (*cp1 == '\0' ? cp2 : cp1);
+}
+
+static void
+verror(n, ap)
+ int n;
+ va_list ap;
+{
+ const char *fn;
+
+ fn = basename(curr_pos.p_file);
+ (void)printf("%s:%d: ", fn, curr_pos.p_line);
+ (void)vprintf(msgs[n], ap);
+ (void)printf("\n");
+ nerr++;
+}
+
+static void
+vwarning(n, ap)
+ int n;
+ va_list ap;
+{
+ const char *fn;
+
+ if (nowarn)
+ /* this warning is suppressed by a LINTED comment */
+ return;
+
+ fn = basename(curr_pos.p_file);
+ (void)printf("%s:%d: warning: ", fn, curr_pos.p_line);
+ (void)vprintf(msgs[n], ap);
+ (void)printf("\n");
+}
+
+void
+#ifdef __STDC__
+error(int n, ...)
+#else
+error(n, va_alist)
+ int n;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#ifdef __STDC__
+ va_start(ap, n);
+#else
+ va_start(ap);
+#endif
+ verror(n, ap);
+ va_end(ap);
+}
+
+void
+#ifdef __STDC__
+lerror(const char *msg, ...)
+#else
+lerror(msg, va_alist)
+ const char *msg;
+ va_dcl
+#endif
+{
+ va_list ap;
+ const char *fn;
+
+#ifdef __STDC__
+ va_start(ap, msg);
+#else
+ va_start(ap);
+#endif
+ fn = basename(curr_pos.p_file);
+ (void)fprintf(stderr, "%s:%d: lint error: ", fn, curr_pos.p_line);
+ (void)vfprintf(stderr, msg, ap);
+ (void)fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+void
+#ifdef __STDC__
+warning(int n, ...)
+#else
+warning(n, va_alist)
+ int n;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#ifdef __STDC__
+ va_start(ap, n);
+#else
+ va_start(ap);
+#endif
+ vwarning(n, ap);
+ va_end(ap);
+}
+
+void
+#ifdef __STDC__
+message(int n, ...)
+#else
+message(n, va_alist)
+ int n;
+ va_dcl
+#endif
+{
+ va_list ap;
+ const char *fn;
+
+#ifdef __STDC__
+ va_start(ap, n);
+#else
+ va_start(ap);
+#endif
+ fn = basename(curr_pos.p_file);
+ (void)printf("%s:%d: ", fn, curr_pos.p_line);
+ (void)vprintf(msgs[n], ap);
+ (void)printf("\n");
+ va_end(ap);
+}
+
+int
+#ifdef __STDC__
+gnuism(int n, ...)
+#else
+gnuism(n, va_alist)
+ int n;
+ va_dcl
+#endif
+{
+ va_list ap;
+ int msg;
+
+#ifdef __STDC__
+ va_start(ap, n);
+#else
+ va_start(ap);
+#endif
+ if (sflag && !gflag) {
+ verror(n, ap);
+ msg = 1;
+ } else if (!sflag && gflag) {
+ msg = 0;
+ } else {
+ vwarning(n, ap);
+ msg = 1;
+ }
+ va_end(ap);
+
+ return (msg);
+}
diff --git a/usr.bin/xlint/lint1/externs.h b/usr.bin/xlint/lint1/externs.h
new file mode 100644
index 0000000..e61f8d0
--- /dev/null
+++ b/usr.bin/xlint/lint1/externs.h
@@ -0,0 +1,56 @@
+/* $NetBSD: externs.h,v 1.2 1995/07/03 21:24:06 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+/*
+ * mem.c
+ */
+extern void *xmalloc __P((size_t));
+extern void *xcalloc __P((size_t, size_t));
+extern void *xrealloc __P((void *, size_t));
+extern char *xstrdup __P((const char *));
+extern void nomem __P((void));
+
+/*
+ * emit.c
+ */
+extern ob_t ob;
+
+extern void outopen __P((const char *));
+extern void outclose __P((void));
+extern void outclr __P((void));
+extern void outchar __P((int));
+extern void outqchar __P((int));
+extern void outstrg __P((const char *));
+extern void outint __P((int));
+extern void outname __P((const char *));
+extern void outsrc __P((const char *));
diff --git a/usr.bin/xlint/lint1/externs1.h b/usr.bin/xlint/lint1/externs1.h
new file mode 100644
index 0000000..e9923c7
--- /dev/null
+++ b/usr.bin/xlint/lint1/externs1.h
@@ -0,0 +1,281 @@
+/* $NetBSD: externs1.h,v 1.7 1995/10/02 17:31:39 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+/*
+ * main.c
+ */
+extern int aflag;
+extern int bflag;
+extern int cflag;
+extern int dflag;
+extern int eflag;
+extern int Fflag;
+extern int gflag;
+extern int hflag;
+extern int pflag;
+extern int rflag;
+extern int sflag;
+extern int tflag;
+extern int uflag;
+extern int vflag;
+extern int yflag;
+extern int zflag;
+
+extern void norecover __P((void));
+
+/*
+ * cgram.y
+ */
+extern int blklev;
+extern int mblklev;
+extern int yydebug;
+
+extern int yyerror __P((char *));
+extern int yyparse __P((void));
+
+/*
+ * scan.l
+ */
+extern pos_t curr_pos;
+extern pos_t csrc_pos;
+extern symt_t symtyp;
+extern FILE *yyin;
+extern u_quad_t qbmasks[], qlmasks[], qumasks[];
+
+extern void initscan __P((void));
+extern int sign __P((quad_t, tspec_t, int));
+extern int msb __P((quad_t, tspec_t, int));
+extern quad_t xsign __P((quad_t, tspec_t, int));
+extern void clrwflgs __P((void));
+extern sym_t *getsym __P((sbuf_t *));
+extern void cleanup __P((void));
+extern sym_t *pushdown __P((sym_t *));
+extern void rmsym __P((sym_t *));
+extern void rmsyms __P((sym_t *));
+extern void inssym __P((int, sym_t *));
+extern void freeyyv __P((void *, int));
+extern int yylex __P((void));
+
+/*
+ * mem1.c
+ */
+extern const char *fnalloc __P((const char *));
+extern const char *fnnalloc __P((const char *, size_t));
+extern int getfnid __P((const char *));
+
+extern void initmem __P((void));
+
+extern void *getblk __P((size_t));
+extern void *getlblk __P((int, size_t));
+extern void freeblk __P((void));
+extern void freelblk __P((int));
+
+extern void *tgetblk __P((size_t));
+extern tnode_t *getnode __P((void));
+extern void tfreeblk __P((void));
+extern struct mbl *tsave __P((void));
+extern void trestor __P((struct mbl *));
+
+/*
+ * err.c
+ */
+extern int nerr;
+extern int sytxerr;
+extern const char *msgs[];
+
+extern void error __P((int, ...));
+extern void warning __P((int, ...));
+extern void message __P((int, ...));
+extern int gnuism __P((int, ...));
+extern void lerror __P((const char *, ...));
+
+/*
+ * decl.c
+ */
+extern dinfo_t *dcs;
+extern const char *unnamed;
+extern int enumval;
+
+extern void initdecl __P((void));
+extern type_t *gettyp __P((tspec_t));
+extern type_t *duptyp __P((const type_t *));
+extern type_t *tduptyp __P((const type_t *));
+extern int incompl __P((type_t *));
+extern void setcompl __P((type_t *, int));
+extern void addscl __P((scl_t));
+extern void addtype __P((type_t *));
+extern void addqual __P((tqual_t));
+extern void pushdecl __P((scl_t));
+extern void popdecl __P((void));
+extern void setasm __P((void));
+extern void clrtyp __P((void));
+extern void deftyp __P((void));
+extern int length __P((type_t *, const char *));
+extern int getbound __P((type_t *));
+extern sym_t *lnklst __P((sym_t *, sym_t *));
+extern void chktyp __P((sym_t *));
+extern sym_t *decl1str __P((sym_t *));
+extern sym_t *bitfield __P((sym_t *, int));
+extern pqinf_t *mergepq __P((pqinf_t *, pqinf_t *));
+extern sym_t *addptr __P((sym_t *, pqinf_t *));
+extern sym_t *addarray __P((sym_t *, int, int));
+extern sym_t *addfunc __P((sym_t *, sym_t *));
+extern void chkfdef __P((sym_t *, int));
+extern sym_t *dname __P((sym_t *));
+extern sym_t *iname __P((sym_t *));
+extern type_t *mktag __P((sym_t *, tspec_t, int, int));
+extern const char *scltoa __P((scl_t));
+extern type_t *compltag __P((type_t *, sym_t *));
+extern sym_t *ename __P((sym_t *, int, int));
+extern void decl1ext __P((sym_t *, int));
+extern void cpuinfo __P((sym_t *, sym_t *));
+extern int isredec __P((sym_t *, int *));
+extern int eqtype __P((type_t *, type_t *, int, int, int *));
+extern void compltyp __P((sym_t *, sym_t *));
+extern sym_t *decl1arg __P((sym_t *, int));
+extern void cluparg __P((void));
+extern void decl1loc __P((sym_t *, int));
+extern sym_t *aname __P((void));
+extern void globclup __P((void));
+extern sym_t *decl1abs __P((sym_t *));
+extern void chksz __P((sym_t *));
+extern void setsflg __P((sym_t *));
+extern void setuflg __P((sym_t *, int, int));
+extern void chkusage __P((dinfo_t *));
+extern void chkusg1 __P((int, sym_t *));
+extern void chkglsyms __P((void));
+extern void prevdecl __P((int, sym_t *));
+
+/*
+ * tree.c
+ */
+extern void initmtab __P((void));
+extern type_t *incref __P((type_t *, tspec_t));
+extern type_t *tincref __P((type_t *, tspec_t));
+extern tnode_t *getcnode __P((type_t *, val_t *));
+extern tnode_t *getnnode __P((sym_t *, int));
+extern tnode_t *getsnode __P((strg_t *));
+extern sym_t *strmemb __P((tnode_t *, op_t, sym_t *));
+extern tnode_t *build __P((op_t, tnode_t *, tnode_t *));
+extern tnode_t *cconv __P((tnode_t *));
+extern int typeok __P((op_t, int, tnode_t *, tnode_t *));
+extern tnode_t *promote __P((op_t, int, tnode_t *));
+extern tnode_t *convert __P((op_t, int, type_t *, tnode_t *));
+extern void cvtcon __P((op_t, int, type_t *, val_t *, val_t *));
+extern const char *tyname __P((type_t *));
+extern tnode_t *bldszof __P((type_t *));
+extern tnode_t *cast __P((tnode_t *, type_t *));
+extern tnode_t *funcarg __P((tnode_t *, tnode_t *));
+extern tnode_t *funccall __P((tnode_t *, tnode_t *));
+extern val_t *constant __P((tnode_t *));
+extern void expr __P((tnode_t *, int, int));
+extern void chkmisc __P((tnode_t *, int, int, int, int, int, int));
+extern int conaddr __P((tnode_t *, sym_t **, ptrdiff_t *));
+extern strg_t *catstrg __P((strg_t *, strg_t *));
+
+/*
+ * func.c
+ */
+extern sym_t *funcsym;
+extern int reached;
+extern int rchflg;
+extern int ftflg;
+extern int nargusg;
+extern pos_t aupos;
+extern int nvararg;
+extern pos_t vapos;
+extern int prflstrg;
+extern pos_t prflpos;
+extern int scflstrg;
+extern pos_t scflpos;
+extern int ccflg;
+extern int llibflg;
+extern int nowarn;
+extern int plibflg;
+extern int quadflg;
+
+extern void pushctrl __P((int));
+extern void popctrl __P((int));
+extern void chkreach __P((void));
+extern void funcdef __P((sym_t *));
+extern void funcend __P((void));
+extern void label __P((int, sym_t *, tnode_t *));
+extern void if1 __P((tnode_t *));
+extern void if2 __P((void));
+extern void if3 __P((int));
+extern void switch1 __P((tnode_t *));
+extern void switch2 __P((void));
+extern void while1 __P((tnode_t *));
+extern void while2 __P((void));
+extern void do1 __P((void));
+extern void do2 __P((tnode_t *));
+extern void for1 __P((tnode_t *, tnode_t *, tnode_t *));
+extern void for2 __P((void));
+extern void dogoto __P((sym_t *));
+extern void docont __P((void));
+extern void dobreak __P((void));
+extern void doreturn __P((tnode_t *));
+extern void glclup __P((int));
+extern void argsused __P((int));
+extern void constcond __P((int));
+extern void fallthru __P((int));
+extern void notreach __P((int));
+extern void lintlib __P((int));
+extern void linted __P((int));
+extern void varargs __P((int));
+extern void printflike __P((int));
+extern void scanflike __P((int));
+extern void protolib __P((int));
+extern void longlong __P((int));
+
+/*
+ * init.c
+ */
+extern int initerr;
+extern sym_t *initsym;
+extern int startinit;
+
+extern void prepinit __P((void));
+extern void initrbr __P((void));
+extern void initlbr __P((void));
+extern void mkinit __P((tnode_t *));
+
+/*
+ * emit.c
+ */
+extern void outtype __P((type_t *));
+extern const char *ttos __P((type_t *));
+extern void outsym __P((sym_t *, scl_t, def_t));
+extern void outfdef __P((sym_t *, pos_t *, int, int, sym_t *));
+extern void outcall __P((tnode_t *, int, int));
+extern void outusg __P((sym_t *));
diff --git a/usr.bin/xlint/lint1/func.c b/usr.bin/xlint/lint1/func.c
new file mode 100644
index 0000000..5d3a172
--- /dev/null
+++ b/usr.bin/xlint/lint1/func.c
@@ -0,0 +1,1261 @@
+/* $NetBSD: func.c,v 1.7 1995/10/02 17:31:40 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: func.c,v 1.7 1995/10/02 17:31:40 jpo Exp $";
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "lint1.h"
+#include "y.tab.h"
+
+/*
+ * Contains a pointer to the symbol table entry of the current function
+ * definition.
+ */
+sym_t *funcsym;
+
+/* Is set as long as a statement can be reached. Must be set at level 0. */
+int reached = 1;
+
+/*
+ * Is set as long as NOTREACHED is in effect.
+ * Is reset everywhere where reached can become 0.
+ */
+int rchflg;
+
+/*
+ * In conjunction with reached ontrols printing of "fallthrough on ..."
+ * warnings.
+ * Reset by each statement and set by FALLTHROUGH, switch (switch1())
+ * and case (label()).
+ *
+ * Control statements if, for, while and switch do not reset ftflg because
+ * this must be done by the controled statement. At least for if this is
+ * important because ** FALLTHROUGH ** after "if (expr) stmnt" is evaluated
+ * befor the following token, wich causes reduction of above, is read.
+ * This means that ** FALLTHROUGH ** after "if ..." would always be ignored.
+ */
+int ftflg;
+
+/* Top element of stack for control statements */
+cstk_t *cstk;
+
+/*
+ * Number of arguments which will be checked for usage in following
+ * function definition. -1 stands for all arguments.
+ *
+ * The position of the last ARGSUSED comment is stored in aupos.
+ */
+int nargusg = -1;
+pos_t aupos;
+
+/*
+ * Number of arguments of the following function definition whose types
+ * shall be checked by lint2. -1 stands for all arguments.
+ *
+ * The position of the last VARARGS comment is stored in vapos.
+ */
+int nvararg = -1;
+pos_t vapos;
+
+/*
+ * Both prflstr and scflstrg contain the number of the argument which
+ * shall be used to check the types of remaining arguments (for PRINTFLIKE
+ * and SCANFLIKE).
+ *
+ * prflpos and scflpos are the positions of the last PRINTFLIKE or
+ * SCANFLIKE comment.
+ */
+int prflstrg = -1;
+int scflstrg = -1;
+pos_t prflpos;
+pos_t scflpos;
+
+/*
+ * Are both plibflg and llibflg set, prototypes are writen as function
+ * definitions to the output file.
+ */
+int plibflg;
+
+/*
+ * Nonzero means that no warnings about constands in conditional
+ * context are printed.
+ */
+int ccflg;
+
+/*
+ * llibflg is set if a lint library shall be created. The effect of
+ * llibflg is that all defined symbols are treated as used.
+ * (The LINTLIBRARY comment also resets vflag.)
+ */
+int llibflg;
+
+/*
+ * Nonzero if warnings are suppressed by a LINTED directive
+ */
+int nowarn;
+
+/*
+ * Nonzero if complaints about use of "long long" are suppressed in
+ * the next statement or declaration.
+ */
+int quadflg;
+
+/*
+ * Puts a new element at the top of the stack used for control statements.
+ */
+void
+pushctrl(env)
+ int env;
+{
+ cstk_t *ci;
+
+ ci = xcalloc(1, sizeof (cstk_t));
+ ci->c_env = env;
+ ci->c_nxt = cstk;
+ cstk = ci;
+}
+
+/*
+ * Removes the top element of the stack used for control statements.
+ */
+void
+popctrl(env)
+ int env;
+{
+ cstk_t *ci;
+ clst_t *cl;
+
+ if (cstk == NULL || cstk->c_env != env)
+ lerror("popctrl() 1");
+
+ cstk = (ci = cstk)->c_nxt;
+
+ while ((cl = ci->c_clst) != NULL) {
+ ci->c_clst = cl->cl_nxt;
+ free(cl);
+ }
+
+ if (ci->c_swtype != NULL)
+ free(ci->c_swtype);
+
+ free(ci);
+}
+
+/*
+ * Prints a warning if a statement cannot be reached.
+ */
+void
+chkreach()
+{
+ if (!reached && !rchflg) {
+ /* statement not reached */
+ warning(193);
+ reached = 1;
+ }
+}
+
+/*
+ * Called after a function declaration which introduces a function definition
+ * and before an (optional) old style argument declaration list.
+ *
+ * Puts all symbols declared in the Prototype or in an old style argument
+ * list back to the symbol table.
+ *
+ * Does the usual checking of storage class, type (return value),
+ * redeclaration etc..
+ */
+void
+funcdef(fsym)
+ sym_t *fsym;
+{
+ int n, warn;
+ sym_t *arg, *sym, *rdsym;
+
+ funcsym = fsym;
+
+ /*
+ * Put all symbols declared in the argument list back to the
+ * symbol table.
+ */
+ for (sym = dcs->d_fpsyms; sym != NULL; sym = sym->s_dlnxt) {
+ if (sym->s_blklev != -1) {
+ if (sym->s_blklev != 1)
+ lerror("funcdef() 1");
+ inssym(1, sym);
+ }
+ }
+
+ /*
+ * In osfunc() we did not know whether it is an old style function
+ * definition or only an old style declaration, if there are no
+ * arguments inside the argument list ("f()").
+ */
+ if (!fsym->s_type->t_proto && fsym->s_args == NULL)
+ fsym->s_osdef = 1;
+
+ chktyp(fsym);
+
+ /*
+ * chktyp() checks for almost all possible errors, but not for
+ * incomplete return values (these are allowed in declarations)
+ */
+ if (fsym->s_type->t_subt->t_tspec != VOID &&
+ incompl(fsym->s_type->t_subt)) {
+ /* cannot return incomplete type */
+ error(67);
+ }
+
+ fsym->s_def = DEF;
+
+ if (fsym->s_scl == TYPEDEF) {
+ fsym->s_scl = EXTERN;
+ /* illegal storage class */
+ error(8);
+ }
+
+ if (dcs->d_inline)
+ fsym->s_inline = 1;
+
+ /*
+ * Arguments in new style function declarations need a name.
+ * (void is already removed from the list of arguments)
+ */
+ n = 1;
+ for (arg = fsym->s_type->t_args; arg != NULL; arg = arg->s_nxt) {
+ if (arg->s_scl == ABSTRACT) {
+ if (arg->s_name != unnamed)
+ lerror("funcdef() 2");
+ /* formal parameter lacks name: param #%d */
+ error(59, n);
+ } else {
+ if (arg->s_name == unnamed)
+ lerror("funcdef() 3");
+ }
+ n++;
+ }
+
+ /*
+ * We must also remember the position. s_dpos is overwritten
+ * if this is an old style definition and we had already a
+ * prototype.
+ */
+ STRUCT_ASSIGN(dcs->d_fdpos, fsym->s_dpos);
+
+ if ((rdsym = dcs->d_rdcsym) != NULL) {
+
+ if (!isredec(fsym, (warn = 0, &warn))) {
+
+ /*
+ * Print nothing if the newly defined function
+ * is defined in old style. A better warning will
+ * be printed in cluparg().
+ */
+ if (warn && !fsym->s_osdef) {
+ /* redeclaration of %s */
+ (*(sflag ? error : warning))(27, fsym->s_name);
+ prevdecl(-1, rdsym);
+ }
+
+ /* copy usage information */
+ cpuinfo(fsym, rdsym);
+
+ /*
+ * If the old symbol was a prototype and the new
+ * one is none, overtake the position of the
+ * declaration of the prototype.
+ */
+ if (fsym->s_osdef && rdsym->s_type->t_proto)
+ STRUCT_ASSIGN(fsym->s_dpos, rdsym->s_dpos);
+
+ /* complete the type */
+ compltyp(fsym, rdsym);
+
+ /* once a function is inline it remains inline */
+ if (rdsym->s_inline)
+ fsym->s_inline = 1;
+
+ }
+
+ /* remove the old symbol from the symbol table */
+ rmsym(rdsym);
+
+ }
+
+ if (fsym->s_osdef && !fsym->s_type->t_proto) {
+ if (sflag && hflag && strcmp(fsym->s_name, "main") != 0)
+ /* function definition is not a prototyp */
+ warning(286);
+ }
+
+ if (dcs->d_notyp)
+ /* return value is implizitly declared to be int */
+ fsym->s_rimpl = 1;
+
+ reached = 1;
+}
+
+/*
+ * Called at the end of a function definition.
+ */
+void
+funcend()
+{
+ sym_t *arg;
+ int n;
+
+ if (reached) {
+ cstk->c_noretval = 1;
+ if (funcsym->s_type->t_subt->t_tspec != VOID &&
+ !funcsym->s_rimpl) {
+ /* func. %s falls off bottom without returning value */
+ warning(217, funcsym->s_name);
+ }
+ }
+
+ /*
+ * This warning is printed only if the return value was implizitly
+ * declared to be int. Otherwise the wrong return statement
+ * has already printed a warning.
+ */
+ if (cstk->c_noretval && cstk->c_retval && funcsym->s_rimpl)
+ /* function %s has return (e); and return; */
+ warning(216, funcsym->s_name);
+
+ /* Print warnings for unused arguments */
+ arg = dcs->d_fargs;
+ n = 0;
+ while (arg != NULL && (nargusg == -1 || n < nargusg)) {
+ chkusg1(dcs->d_asm, arg);
+ arg = arg->s_nxt;
+ n++;
+ }
+ nargusg = -1;
+
+ /*
+ * write the information about the function definition to the
+ * output file
+ * inline functions explicitely declared extern are written as
+ * declarations only.
+ */
+ if (dcs->d_scl == EXTERN && funcsym->s_inline) {
+ outsym(funcsym, funcsym->s_scl, DECL);
+ } else {
+ outfdef(funcsym, &dcs->d_fdpos, cstk->c_retval,
+ funcsym->s_osdef, dcs->d_fargs);
+ }
+
+ /*
+ * remove all symbols declared during argument declaration from
+ * the symbol table
+ */
+ if (dcs->d_nxt != NULL || dcs->d_ctx != EXTERN)
+ lerror("funcend() 1");
+ rmsyms(dcs->d_fpsyms);
+
+ /* must be set on level 0 */
+ reached = 1;
+}
+
+/*
+ * Process a label.
+ *
+ * typ type of the label (T_NAME, T_DEFAULT or T_CASE).
+ * sym symbol table entry of label if typ == T_NAME
+ * tn expression if typ == T_CASE
+ */
+void
+label(typ, sym, tn)
+ int typ;
+ sym_t *sym;
+ tnode_t *tn;
+{
+ cstk_t *ci;
+ clst_t *cl;
+ val_t *v, *nv;
+ tspec_t t;
+
+ switch (typ) {
+
+ case T_NAME:
+ if (sym->s_set) {
+ /* label %s redefined */
+ error(194, sym->s_name);
+ } else {
+ setsflg(sym);
+ }
+ break;
+
+ case T_CASE:
+
+ /* find the stack entry for the innermost switch statement */
+ for (ci = cstk; ci != NULL && !ci->c_switch; ci = ci->c_nxt) ;
+
+ if (ci == NULL) {
+ /* case not in switch */
+ error(195);
+ tn = NULL;
+ } else if (tn != NULL && tn->tn_op != CON) {
+ /* non-constant case expression */
+ error(197);
+ tn = NULL;
+ } else if (tn != NULL && !isityp(tn->tn_type->t_tspec)) {
+ /* non-integral case expression */
+ error(198);
+ tn = NULL;
+ }
+
+ if (tn != NULL) {
+
+ if (ci->c_swtype == NULL)
+ lerror("label() 1");
+
+ if (reached && !ftflg) {
+ if (hflag)
+ /* fallthrough on case statement */
+ warning(220);
+ }
+
+ t = tn->tn_type->t_tspec;
+ if (t == LONG || t == ULONG ||
+ t == QUAD || t == UQUAD) {
+ if (tflag)
+ /* case label must be of type ... */
+ warning(203);
+ }
+
+ /*
+ * get the value of the expression and convert it
+ * to the type of the switch expression
+ */
+ v = constant(tn);
+ nv = xcalloc(1, sizeof (val_t));
+ cvtcon(CASE, 0, ci->c_swtype, nv, v);
+ free(v);
+
+ /* look if we had this value already */
+ for (cl = ci->c_clst; cl != NULL; cl = cl->cl_nxt) {
+ if (cl->cl_val.v_quad == nv->v_quad)
+ break;
+ }
+ if (cl != NULL && isutyp(nv->v_tspec)) {
+ /* duplicate case in switch, %lu */
+ error(200, (u_long)nv->v_quad);
+ } else if (cl != NULL) {
+ /* duplicate case in switch, %ld */
+ error(199, (long)nv->v_quad);
+ } else {
+ /*
+ * append the value to the list of
+ * case values
+ */
+ cl = xcalloc(1, sizeof (clst_t));
+ STRUCT_ASSIGN(cl->cl_val, *nv);
+ cl->cl_nxt = ci->c_clst;
+ ci->c_clst = cl;
+ }
+ }
+ tfreeblk();
+ break;
+
+ case T_DEFAULT:
+
+ /* find the stack entry for the innermost switch statement */
+ for (ci = cstk; ci != NULL && !ci->c_switch; ci = ci->c_nxt) ;
+
+ if (ci == NULL) {
+ /* default outside switch */
+ error(201);
+ } else if (ci->c_default) {
+ /* duplicate default in switch */
+ error(202);
+ } else {
+ if (reached && !ftflg) {
+ if (hflag)
+ /* fallthrough on default statement */
+ warning(284);
+ }
+ ci->c_default = 1;
+ }
+ break;
+ };
+ reached = 1;
+}
+
+/*
+ * T_IF T_LPARN expr T_RPARN
+ */
+void
+if1(tn)
+ tnode_t *tn;
+{
+ if (tn != NULL)
+ tn = cconv(tn);
+ if (tn != NULL)
+ tn = promote(NOOP, 0, tn);
+ expr(tn, 0, 1);
+ pushctrl(T_IF);
+}
+
+/*
+ * if_without_else
+ * if_without_else T_ELSE
+ */
+void
+if2()
+{
+ cstk->c_rchif = reached ? 1 : 0;
+ reached = 1;
+}
+
+/*
+ * if_without_else
+ * if_without_else T_ELSE stmnt
+ */
+void
+if3(els)
+ int els;
+{
+ if (els) {
+ reached |= cstk->c_rchif;
+ } else {
+ reached = 1;
+ }
+ popctrl(T_IF);
+}
+
+/*
+ * T_SWITCH T_LPARN expr T_RPARN
+ */
+void
+switch1(tn)
+ tnode_t *tn;
+{
+ tspec_t t;
+ type_t *tp;
+
+ if (tn != NULL)
+ tn = cconv(tn);
+ if (tn != NULL)
+ tn = promote(NOOP, 0, tn);
+ if (tn != NULL && !isityp(tn->tn_type->t_tspec)) {
+ /* switch expression must have integral type */
+ error(205);
+ tn = NULL;
+ }
+ if (tn != NULL && tflag) {
+ t = tn->tn_type->t_tspec;
+ if (t == LONG || t == ULONG || t == QUAD || t == UQUAD) {
+ /* switch expr. must be of type `int' in trad. C */
+ warning(271);
+ }
+ }
+
+ /*
+ * Remember the type of the expression. Because its possible
+ * that (*tp) is allocated on tree memory the type must be
+ * duplicated. This is not too complicated because it is
+ * only an integer type.
+ */
+ tp = xcalloc(1, sizeof (type_t));
+ if (tn != NULL) {
+ tp->t_tspec = tn->tn_type->t_tspec;
+ if ((tp->t_isenum = tn->tn_type->t_isenum) != 0)
+ tp->t_enum = tn->tn_type->t_enum;
+ } else {
+ tp->t_tspec = INT;
+ }
+
+ expr(tn, 1, 0);
+
+ pushctrl(T_SWITCH);
+ cstk->c_switch = 1;
+ cstk->c_swtype = tp;
+
+ reached = rchflg = 0;
+ ftflg = 1;
+}
+
+/*
+ * switch_expr stmnt
+ */
+void
+switch2()
+{
+ int nenum, nclab;
+ sym_t *esym;
+ clst_t *cl;
+
+ if (cstk->c_swtype == NULL)
+ lerror("switch2() 1");
+
+ /*
+ * If the switch expression was of type enumeration, count the case
+ * labels and the number of enumerators. If both counts are not
+ * equal print a warning.
+ */
+ if (cstk->c_swtype->t_isenum) {
+ nenum = nclab = 0;
+ if (cstk->c_swtype->t_enum == NULL)
+ lerror("switch2() 2");
+ for (esym = cstk->c_swtype->t_enum->elem;
+ esym != NULL; esym = esym->s_nxt) {
+ nenum++;
+ }
+ for (cl = cstk->c_clst; cl != NULL; cl = cl->cl_nxt)
+ nclab++;
+ if (hflag && eflag && nenum != nclab && !cstk->c_default) {
+ /* enumeration value(s) not handled in switch */
+ warning(206);
+ }
+ }
+
+ if (cstk->c_break) {
+ /*
+ * end of switch alway reached (c_break is only set if the
+ * break statement can be reached).
+ */
+ reached = 1;
+ } else if (!cstk->c_default &&
+ (!hflag || !cstk->c_swtype->t_isenum || nenum != nclab)) {
+ /*
+ * there are possible values which are not handled in
+ * switch
+ */
+ reached = 1;
+ } /*
+ * otherwise the end of the switch expression is reached
+ * if the end of the last statement inside it is reached.
+ */
+
+ popctrl(T_SWITCH);
+}
+
+/*
+ * T_WHILE T_LPARN expr T_RPARN
+ */
+void
+while1(tn)
+ tnode_t *tn;
+{
+ if (!reached) {
+ /* loop not entered at top */
+ warning(207);
+ reached = 1;
+ }
+
+ if (tn != NULL)
+ tn = cconv(tn);
+ if (tn != NULL)
+ tn = promote(NOOP, 0, tn);
+ if (tn != NULL && !issclt(tn->tn_type->t_tspec)) {
+ /* controlling expressions must have scalar type */
+ error(204);
+ tn = NULL;
+ }
+
+ pushctrl(T_WHILE);
+ cstk->c_loop = 1;
+ if (tn != NULL && tn->tn_op == CON) {
+ if (isityp(tn->tn_type->t_tspec)) {
+ cstk->c_infinite = tn->tn_val->v_quad != 0;
+ } else {
+ cstk->c_infinite = tn->tn_val->v_ldbl != 0.0;
+ }
+ }
+
+ expr(tn, 0, 1);
+}
+
+/*
+ * while_expr stmnt
+ * while_expr error
+ */
+void
+while2()
+{
+ /*
+ * The end of the loop can be reached if it is no endless loop
+ * or there was a break statement which was reached.
+ */
+ reached = !cstk->c_infinite || cstk->c_break;
+ rchflg = 0;
+
+ popctrl(T_WHILE);
+}
+
+/*
+ * T_DO
+ */
+void
+do1()
+{
+ if (!reached) {
+ /* loop not entered at top */
+ warning(207);
+ reached = 1;
+ }
+
+ pushctrl(T_DO);
+ cstk->c_loop = 1;
+}
+
+/*
+ * do stmnt do_while_expr
+ * do error
+ */
+void
+do2(tn)
+ tnode_t *tn;
+{
+ /*
+ * If there was a continue statement the expression controlling the
+ * loop is reached.
+ */
+ if (cstk->c_cont)
+ reached = 1;
+
+ if (tn != NULL)
+ tn = cconv(tn);
+ if (tn != NULL)
+ tn = promote(NOOP, 0, tn);
+ if (tn != NULL && !issclt(tn->tn_type->t_tspec)) {
+ /* controlling expressions must have scalar type */
+ error(204);
+ tn = NULL;
+ }
+
+ if (tn != NULL && tn->tn_op == CON) {
+ if (isityp(tn->tn_type->t_tspec)) {
+ cstk->c_infinite = tn->tn_val->v_quad != 0;
+ } else {
+ cstk->c_infinite = tn->tn_val->v_ldbl != 0.0;
+ }
+ }
+
+ expr(tn, 0, 1);
+
+ /*
+ * The end of the loop is only reached if it is no endless loop
+ * or there was a break statement which could be reached.
+ */
+ reached = !cstk->c_infinite || cstk->c_break;
+ rchflg = 0;
+
+ popctrl(T_DO);
+}
+
+/*
+ * T_FOR T_LPARN opt_expr T_SEMI opt_expr T_SEMI opt_expr T_RPARN
+ */
+void
+for1(tn1, tn2, tn3)
+ tnode_t *tn1, *tn2, *tn3;
+{
+ /*
+ * If there is no initialisation expression it is possible that
+ * it is intended not to enter the loop at top.
+ */
+ if (tn1 != NULL && !reached) {
+ /* loop not entered at top */
+ warning(207);
+ reached = 1;
+ }
+
+ pushctrl(T_FOR);
+ cstk->c_loop = 1;
+
+ /*
+ * Store the tree memory for the reinitialisation expression.
+ * Also remember this expression itself. We must check it at
+ * the end of the loop to get "used but not set" warnings correct.
+ */
+ cstk->c_fexprm = tsave();
+ cstk->c_f3expr = tn3;
+ STRUCT_ASSIGN(cstk->c_fpos, curr_pos);
+ STRUCT_ASSIGN(cstk->c_cfpos, csrc_pos);
+
+ if (tn1 != NULL)
+ expr(tn1, 0, 0);
+
+ if (tn2 != NULL)
+ tn2 = cconv(tn2);
+ if (tn2 != NULL)
+ tn2 = promote(NOOP, 0, tn2);
+ if (tn2 != NULL && !issclt(tn2->tn_type->t_tspec)) {
+ /* controlling expressions must have scalar type */
+ error(204);
+ tn2 = NULL;
+ }
+ if (tn2 != NULL)
+ expr(tn2, 0, 1);
+
+ if (tn2 == NULL) {
+ cstk->c_infinite = 1;
+ } else if (tn2->tn_op == CON) {
+ if (isityp(tn2->tn_type->t_tspec)) {
+ cstk->c_infinite = tn2->tn_val->v_quad != 0;
+ } else {
+ cstk->c_infinite = tn2->tn_val->v_ldbl != 0.0;
+ }
+ }
+
+ /* Checking the reinitialisation expression is done in for2() */
+
+ reached = 1;
+}
+
+/*
+ * for_exprs stmnt
+ * for_exprs error
+ */
+void
+for2()
+{
+ pos_t cpos, cspos;
+ tnode_t *tn3;
+
+ if (cstk->c_cont)
+ reached = 1;
+
+ STRUCT_ASSIGN(cpos, curr_pos);
+ STRUCT_ASSIGN(cspos, csrc_pos);
+
+ /* Restore the tree memory for the reinitialisation expression */
+ trestor(cstk->c_fexprm);
+ tn3 = cstk->c_f3expr;
+ STRUCT_ASSIGN(curr_pos, cstk->c_fpos);
+ STRUCT_ASSIGN(csrc_pos, cstk->c_cfpos);
+
+ /* simply "statement not reached" would be confusing */
+ if (!reached && !rchflg) {
+ /* end-of-loop code not reached */
+ warning(223);
+ reached = 1;
+ }
+
+ if (tn3 != NULL) {
+ expr(tn3, 0, 0);
+ } else {
+ tfreeblk();
+ }
+
+ STRUCT_ASSIGN(curr_pos, cpos);
+ STRUCT_ASSIGN(csrc_pos, cspos);
+
+ /* An endless loop without break will never terminate */
+ reached = cstk->c_break || !cstk->c_infinite;
+ rchflg = 0;
+
+ popctrl(T_FOR);
+}
+
+/*
+ * T_GOTO identifier T_SEMI
+ * T_GOTO error T_SEMI
+ */
+void
+dogoto(lab)
+ sym_t *lab;
+{
+ setuflg(lab, 0, 0);
+
+ chkreach();
+
+ reached = rchflg = 0;
+}
+
+/*
+ * T_BREAK T_SEMI
+ */
+void
+dobreak()
+{
+ cstk_t *ci;
+
+ ci = cstk;
+ while (ci != NULL && !ci->c_loop && !ci->c_switch)
+ ci = ci->c_nxt;
+
+ if (ci == NULL) {
+ /* break outside loop or switch */
+ error(208);
+ } else {
+ if (reached)
+ ci->c_break = 1;
+ }
+
+ if (bflag)
+ chkreach();
+
+ reached = rchflg = 0;
+}
+
+/*
+ * T_CONTINUE T_SEMI
+ */
+void
+docont()
+{
+ cstk_t *ci;
+
+ for (ci = cstk; ci != NULL && !ci->c_loop; ci = ci->c_nxt) ;
+
+ if (ci == NULL) {
+ /* continue outside loop */
+ error(209);
+ } else {
+ ci->c_cont = 1;
+ }
+
+ chkreach();
+
+ reached = rchflg = 0;
+}
+
+/*
+ * T_RETURN T_SEMI
+ * T_RETURN expr T_SEMI
+ */
+void
+doreturn(tn)
+ tnode_t *tn;
+{
+ tnode_t *ln, *rn;
+ cstk_t *ci;
+ op_t op;
+
+ for (ci = cstk; ci->c_nxt != NULL; ci = ci->c_nxt) ;
+
+ if (tn != NULL) {
+ ci->c_retval = 1;
+ } else {
+ ci->c_noretval = 1;
+ }
+
+ if (tn != NULL && funcsym->s_type->t_subt->t_tspec == VOID) {
+ /* void function %s cannot return value */
+ error(213, funcsym->s_name);
+ tfreeblk();
+ tn = NULL;
+ } else if (tn == NULL && funcsym->s_type->t_subt->t_tspec != VOID) {
+ /*
+ * Assume that the function has a return value only if it
+ * is explicitly declared.
+ */
+ if (!funcsym->s_rimpl)
+ /* function %s expects to return value */
+ warning(214, funcsym->s_name);
+ }
+
+ if (tn != NULL) {
+
+ /* Create a temporary node for the left side */
+ ln = tgetblk(sizeof (tnode_t));
+ ln->tn_op = NAME;
+ ln->tn_type = tduptyp(funcsym->s_type->t_subt);
+ ln->tn_type->t_const = 0;
+ ln->tn_lvalue = 1;
+ ln->tn_sym = funcsym; /* better than nothing */
+
+ tn = build(RETURN, ln, tn);
+
+ if (tn != NULL) {
+ rn = tn->tn_right;
+ while ((op = rn->tn_op) == CVT || op == PLUS)
+ rn = rn->tn_left;
+ if (rn->tn_op == AMPER && rn->tn_left->tn_op == NAME &&
+ rn->tn_left->tn_sym->s_scl == AUTO) {
+ /* %s returns pointer to automatic object */
+ warning(302, funcsym->s_name);
+ }
+ }
+
+ expr(tn, 1, 0);
+
+ } else {
+
+ chkreach();
+
+ }
+
+ reached = rchflg = 0;
+}
+
+/*
+ * Do some cleanup after a global declaration or definition.
+ * Especially remove informations about unused lint comments.
+ */
+void
+glclup(silent)
+ int silent;
+{
+ pos_t cpos;
+
+ STRUCT_ASSIGN(cpos, curr_pos);
+
+ if (nargusg != -1) {
+ if (!silent) {
+ STRUCT_ASSIGN(curr_pos, aupos);
+ /* must precede function definition: %s */
+ warning(282, "ARGSUSED");
+ }
+ nargusg = -1;
+ }
+ if (nvararg != -1) {
+ if (!silent) {
+ STRUCT_ASSIGN(curr_pos, vapos);
+ /* must precede function definition: %s */
+ warning(282, "VARARGS");
+ }
+ nvararg = -1;
+ }
+ if (prflstrg != -1) {
+ if (!silent) {
+ STRUCT_ASSIGN(curr_pos, prflpos);
+ /* must precede function definition: %s */
+ warning(282, "PRINTFLIKE");
+ }
+ prflstrg = -1;
+ }
+ if (scflstrg != -1) {
+ if (!silent) {
+ STRUCT_ASSIGN(curr_pos, scflpos);
+ /* must precede function definition: %s */
+ warning(282, "SCANFLIKE");
+ }
+ scflstrg = -1;
+ }
+
+ STRUCT_ASSIGN(curr_pos, cpos);
+
+ dcs->d_asm = 0;
+}
+
+/*
+ * ARGSUSED comment
+ *
+ * Only the first n arguments of the following function are checked
+ * for usage. A missing argument is taken to be 0.
+ */
+void
+argsused(n)
+ int n;
+{
+ if (n == -1)
+ n = 0;
+
+ if (dcs->d_ctx != EXTERN) {
+ /* must be outside function: ** %s ** */
+ warning(280, "ARGSUSED");
+ return;
+ }
+ if (nargusg != -1) {
+ /* duplicate use of ** %s ** */
+ warning(281, "ARGSUSED");
+ }
+ nargusg = n;
+ STRUCT_ASSIGN(aupos, curr_pos);
+}
+
+/*
+ * VARARGS comment
+ *
+ * Makes that lint2 checks only the first n arguments for compatibility
+ * to the function definition. A missing argument is taken to be 0.
+ */
+void
+varargs(n)
+ int n;
+{
+ if (n == -1)
+ n = 0;
+
+ if (dcs->d_ctx != EXTERN) {
+ /* must be outside function: ** %s ** */
+ warning(280, "VARARGS");
+ return;
+ }
+ if (nvararg != -1) {
+ /* duplicate use of ** %s ** */
+ warning(281, "VARARGS");
+ }
+ nvararg = n;
+ STRUCT_ASSIGN(vapos, curr_pos);
+}
+
+/*
+ * PRINTFLIKE comment
+ *
+ * Check all arguments until the (n-1)-th as usual. The n-th argument is
+ * used the check the types of remaining arguments.
+ */
+void
+printflike(n)
+ int n;
+{
+ if (n == -1)
+ n = 0;
+
+ if (dcs->d_ctx != EXTERN) {
+ /* must be outside function: ** %s ** */
+ warning(280, "PRINTFLIKE");
+ return;
+ }
+ if (prflstrg != -1) {
+ /* duplicate use of ** %s ** */
+ warning(281, "PRINTFLIKE");
+ }
+ prflstrg = n;
+ STRUCT_ASSIGN(prflpos, curr_pos);
+}
+
+/*
+ * SCANFLIKE comment
+ *
+ * Check all arguments until the (n-1)-th as usual. The n-th argument is
+ * used the check the types of remaining arguments.
+ */
+void
+scanflike(n)
+ int n;
+{
+ if (n == -1)
+ n = 0;
+
+ if (dcs->d_ctx != EXTERN) {
+ /* must be outside function: ** %s ** */
+ warning(280, "SCANFLIKE");
+ return;
+ }
+ if (scflstrg != -1) {
+ /* duplicate use of ** %s ** */
+ warning(281, "SCANFLIKE");
+ }
+ scflstrg = n;
+ STRUCT_ASSIGN(scflpos, curr_pos);
+}
+
+/*
+ * Set the linenumber for a CONSTCOND comment. At this and the following
+ * line no warnings about constants in conditional contexts are printed.
+ */
+/* ARGSUSED */
+void
+constcond(n)
+ int n;
+{
+ ccflg = 1;
+}
+
+/*
+ * Suppress printing of "fallthrough on ..." warnings until next
+ * statement.
+ */
+/* ARGSUSED */
+void
+fallthru(n)
+ int n;
+{
+ ftflg = 1;
+}
+
+/*
+ * Stop warnings about statements which cannot be reached. Also tells lint
+ * that the following statements cannot be reached (e.g. after exit()).
+ */
+/* ARGSUSED */
+void
+notreach(n)
+ int n;
+{
+ reached = 0;
+ rchflg = 1;
+}
+
+/* ARGSUSED */
+void
+lintlib(n)
+ int n;
+{
+ if (dcs->d_ctx != EXTERN) {
+ /* must be outside function: ** %s ** */
+ warning(280, "LINTLIBRARY");
+ return;
+ }
+ llibflg = 1;
+ vflag = 0;
+}
+
+/*
+ * Suppress most warnings at the current and the following line.
+ */
+/* ARGSUSED */
+void
+linted(n)
+ int n;
+{
+ nowarn = 1;
+}
+
+/*
+ * PROTOTLIB in conjunction with LINTLIBRARY can be used to handle
+ * prototypes like function definitions. This is done if the argument
+ * to PROTOLIB is nonzero. Otherwise prototypes are handled normaly.
+ */
+void
+protolib(n)
+ int n;
+{
+ if (dcs->d_ctx != EXTERN) {
+ /* must be outside function: ** %s ** */
+ warning(280, "PROTOLIB");
+ return;
+ }
+ plibflg = n == 0 ? 0 : 1;
+}
+
+/*
+ * Set quadflg to nonzero which means that the next statement/declaration
+ * may use "long long" without an error or warning.
+ */
+/* ARGSUSED */
+void
+longlong(n)
+ int n;
+{
+ quadflg = 1;
+}
diff --git a/usr.bin/xlint/lint1/init.c b/usr.bin/xlint/lint1/init.c
new file mode 100644
index 0000000..dbd216b
--- /dev/null
+++ b/usr.bin/xlint/lint1/init.c
@@ -0,0 +1,513 @@
+/* $NetBSD: init.c,v 1.4 1995/10/02 17:21:37 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: init.c,v 1.4 1995/10/02 17:21:37 jpo Exp $";
+#endif
+
+#include <stdlib.h>
+
+#include "lint1.h"
+
+/*
+ * initerr is set as soon as a fatal error occured in an initialisation.
+ * The effect is that the rest of the initialisation is ignored (parsed
+ * by yacc, expression trees built, but no initialisation takes place).
+ */
+int initerr;
+
+/* Pointer to the symbol which is to be initialized. */
+sym_t *initsym;
+
+/* Points to the top element of the initialisation stack. */
+istk_t *initstk;
+
+
+static void popi2 __P((void));
+static void popinit __P((int));
+static void pushinit __P((void));
+static void testinit __P((void));
+static void nextinit __P((int));
+static int strginit __P((tnode_t *));
+
+
+/*
+ * Initialize the initialisation stack by putting an entry for the variable
+ * which is to be initialized on it.
+ */
+void
+prepinit()
+{
+ istk_t *istk;
+
+ if (initerr)
+ return;
+
+ /* free memory used in last initialisation */
+ while ((istk = initstk) != NULL) {
+ initstk = istk->i_nxt;
+ free(istk);
+ }
+
+ /*
+ * If the type which is to be initialized is an incomplete type,
+ * it must be duplicated.
+ */
+ if (initsym->s_type->t_tspec == ARRAY && incompl(initsym->s_type))
+ initsym->s_type = duptyp(initsym->s_type);
+
+ istk = initstk = xcalloc(1, sizeof (istk_t));
+ istk->i_subt = initsym->s_type;
+ istk->i_cnt = 1;
+
+}
+
+static void
+popi2()
+{
+ istk_t *istk;
+ sym_t *m;
+
+ initstk = (istk = initstk)->i_nxt;
+ if (initstk == NULL)
+ lerror("popi2() 1");
+ free(istk);
+
+ istk = initstk;
+
+ istk->i_cnt--;
+ if (istk->i_cnt < 0)
+ lerror("popi2() 3");
+
+ /*
+ * If the removed element was a structure member, we must go
+ * to the next structure member.
+ */
+ if (istk->i_cnt > 0 && istk->i_type->t_tspec == STRUCT) {
+ do {
+ m = istk->i_mem = istk->i_mem->s_nxt;
+ if (m == NULL)
+ lerror("popi2() 2");
+ } while (m->s_field && m->s_name == unnamed);
+ istk->i_subt = m->s_type;
+ }
+}
+
+static void
+popinit(brace)
+ int brace;
+{
+ if (brace) {
+ /*
+ * Take all entries, including the first which requires
+ * a closing brace, from the stack.
+ */
+ do {
+ brace = initstk->i_brace;
+ popi2();
+ } while (!brace);
+ } else {
+ /*
+ * Take all entries which cannot be used for further
+ * initializers from the stack, but do this only if
+ * they do not require a closing brace.
+ */
+ while (!initstk->i_brace &&
+ initstk->i_cnt == 0 && !initstk->i_nolimit) {
+ popi2();
+ }
+ }
+}
+
+static void
+pushinit()
+{
+ istk_t *istk;
+ int cnt;
+ sym_t *m;
+
+ istk = initstk;
+
+ /* Extend an incomplete array type by one element */
+ if (istk->i_cnt == 0) {
+ /*
+ * Inside of other aggregate types must not be an incomplete
+ * type.
+ */
+ if (istk->i_nxt->i_nxt != NULL)
+ lerror("pushinit() 1");
+ istk->i_cnt = 1;
+ if (istk->i_type->t_tspec != ARRAY)
+ lerror("pushinit() 2");
+ istk->i_type->t_dim++;
+ /* from now its an complete type */
+ setcompl(istk->i_type, 0);
+ }
+
+ if (istk->i_cnt <= 0)
+ lerror("pushinit() 3");
+ if (istk->i_type != NULL && issclt(istk->i_type->t_tspec))
+ lerror("pushinit() 4");
+
+ initstk = xcalloc(1, sizeof (istk_t));
+ initstk->i_nxt = istk;
+ initstk->i_type = istk->i_subt;
+ if (initstk->i_type->t_tspec == FUNC)
+ lerror("pushinit() 5");
+
+ istk = initstk;
+
+ switch (istk->i_type->t_tspec) {
+ case ARRAY:
+ if (incompl(istk->i_type) && istk->i_nxt->i_nxt != NULL) {
+ /* initialisation of an incomplete type */
+ error(175);
+ initerr = 1;
+ return;
+ }
+ istk->i_subt = istk->i_type->t_subt;
+ istk->i_nolimit = incompl(istk->i_type);
+ istk->i_cnt = istk->i_type->t_dim;
+ break;
+ case UNION:
+ if (tflag)
+ /* initialisation of union is illegal in trad. C */
+ warning(238);
+ /* FALLTHROUGH */
+ case STRUCT:
+ if (incompl(istk->i_type)) {
+ /* initialisation of an incomplete type */
+ error(175);
+ initerr = 1;
+ return;
+ }
+ cnt = 0;
+ for (m = istk->i_type->t_str->memb; m != NULL; m = m->s_nxt) {
+ if (m->s_field && m->s_name == unnamed)
+ continue;
+ if (++cnt == 1) {
+ istk->i_mem = m;
+ istk->i_subt = m->s_type;
+ }
+ }
+ if (cnt == 0) {
+ /* cannot init. struct/union with no named member */
+ error(179);
+ initerr = 1;
+ return;
+ }
+ istk->i_cnt = istk->i_type->t_tspec == STRUCT ? cnt : 1;
+ break;
+ default:
+ istk->i_cnt = 1;
+ break;
+ }
+}
+
+static void
+testinit()
+{
+ istk_t *istk;
+
+ istk = initstk;
+
+ /*
+ * If a closing brace is expected we have at least one initializer
+ * too much.
+ */
+ if (istk->i_cnt == 0 && !istk->i_nolimit) {
+ switch (istk->i_type->t_tspec) {
+ case ARRAY:
+ /* too many array initializers */
+ error(173);
+ break;
+ case STRUCT:
+ case UNION:
+ /* too many struct/union initializers */
+ error(172);
+ break;
+ default:
+ /* too many initializers */
+ error(174);
+ break;
+ }
+ initerr = 1;
+ }
+}
+
+static void
+nextinit(brace)
+ int brace;
+{
+ if (!brace) {
+ if (initstk->i_type == NULL &&
+ !issclt(initstk->i_subt->t_tspec)) {
+ /* {}-enclosed initializer required */
+ error(181);
+ }
+ /*
+ * Make sure an entry with a scalar type is at the top
+ * of the stack.
+ */
+ if (!initerr)
+ testinit();
+ while (!initerr && (initstk->i_type == NULL ||
+ !issclt(initstk->i_type->t_tspec))) {
+ if (!initerr)
+ pushinit();
+ }
+ } else {
+ if (initstk->i_type != NULL &&
+ issclt(initstk->i_type->t_tspec)) {
+ /* invalid initializer */
+ error(176);
+ initerr = 1;
+ }
+ if (!initerr)
+ testinit();
+ if (!initerr)
+ pushinit();
+ if (!initerr)
+ initstk->i_brace = 1;
+ }
+}
+
+void
+initlbr()
+{
+ if (initerr)
+ return;
+
+ if ((initsym->s_scl == AUTO || initsym->s_scl == REG) &&
+ initstk->i_nxt == NULL) {
+ if (tflag && !issclt(initstk->i_subt->t_tspec))
+ /* no automatic aggregate initialization in trad. C*/
+ warning(188);
+ }
+
+ /*
+ * Remove all entries which cannot be used for further initializers
+ * and do not expect a closing brace.
+ */
+ popinit(0);
+
+ nextinit(1);
+}
+
+void
+initrbr()
+{
+ if (initerr)
+ return;
+
+ popinit(1);
+}
+
+void
+mkinit(tn)
+ tnode_t *tn;
+{
+ ptrdiff_t offs;
+ sym_t *sym;
+ tspec_t lt, rt;
+ tnode_t *ln;
+ struct mbl *tmem;
+ scl_t sc;
+
+ if (initerr || tn == NULL)
+ goto end;
+
+ sc = initsym->s_scl;
+
+ /*
+ * Do not test for automatic aggregat initialisation. If the
+ * initalizer starts with a brace we have the warning already.
+ * If not, an error will be printed that the initializer must
+ * be enclosed by braces.
+ */
+
+ /*
+ * Local initialisation of non-array-types with only one expression
+ * without braces is done by ASSIGN
+ */
+ if ((sc == AUTO || sc == REG) &&
+ initsym->s_type->t_tspec != ARRAY && initstk->i_nxt == NULL) {
+ ln = getnnode(initsym, 0);
+ ln->tn_type = tduptyp(ln->tn_type);
+ ln->tn_type->t_const = 0;
+ tn = build(ASSIGN, ln, tn);
+ expr(tn, 0, 0);
+ goto end;
+ }
+
+ /*
+ * Remove all entries which cannot be used for further initializers
+ * and do not require a closing brace.
+ */
+ popinit(0);
+
+ /* Initialisations by strings are done in strginit(). */
+ if (strginit(tn))
+ goto end;
+
+ nextinit(0);
+ if (initerr || tn == NULL)
+ goto end;
+
+ initstk->i_cnt--;
+
+ /* Create a temporary node for the left side. */
+ ln = tgetblk(sizeof (tnode_t));
+ ln->tn_op = NAME;
+ ln->tn_type = tduptyp(initstk->i_type);
+ ln->tn_type->t_const = 0;
+ ln->tn_lvalue = 1;
+ ln->tn_sym = initsym; /* better than nothing */
+
+ tn = cconv(tn);
+
+ lt = ln->tn_type->t_tspec;
+ rt = tn->tn_type->t_tspec;
+
+ if (!issclt(lt))
+ lerror("mkinit() 1");
+
+ if (!typeok(INIT, 0, ln, tn))
+ goto end;
+
+ /*
+ * Store the tree memory. This is nessesary because otherwise
+ * expr() would free it.
+ */
+ tmem = tsave();
+ expr(tn, 1, 0);
+ trestor(tmem);
+
+ if (isityp(lt) && ln->tn_type->t_isfield && !isityp(rt)) {
+ /*
+ * Bit-fields can be initialized in trad. C only by integer
+ * constants.
+ */
+ if (tflag)
+ /* bit-field initialisation is illegal in trad. C */
+ warning(186);
+ }
+
+ if (lt != rt || (initstk->i_type->t_isfield && tn->tn_op == CON))
+ tn = convert(INIT, 0, initstk->i_type, tn);
+
+ if (tn != NULL && tn->tn_op != CON) {
+ sym = NULL;
+ offs = 0;
+ if (conaddr(tn, &sym, &offs) == -1) {
+ if (sc == AUTO || sc == REG) {
+ /* non-constant initializer */
+ (void)gnuism(177);
+ } else {
+ /* non-constant initializer */
+ error(177);
+ }
+ }
+ }
+
+ end:
+ tfreeblk();
+}
+
+
+static int
+strginit(tn)
+ tnode_t *tn;
+{
+ tspec_t t;
+ istk_t *istk;
+ int len;
+ strg_t *strg;
+
+ if (tn->tn_op != STRING)
+ return (0);
+
+ istk = initstk;
+ strg = tn->tn_strg;
+
+ /*
+ * Check if we have an array type which can be initialized by
+ * the string.
+ */
+ if (istk->i_subt->t_tspec == ARRAY) {
+ t = istk->i_subt->t_subt->t_tspec;
+ if (!((strg->st_tspec == CHAR &&
+ (t == CHAR || t == UCHAR || t == SCHAR)) ||
+ (strg->st_tspec == WCHAR && t == WCHAR))) {
+ return (0);
+ }
+ /* Put the array at top of stack */
+ pushinit();
+ istk = initstk;
+ } else if (istk->i_type != NULL && istk->i_type->t_tspec == ARRAY) {
+ t = istk->i_type->t_subt->t_tspec;
+ if (!((strg->st_tspec == CHAR &&
+ (t == CHAR || t == UCHAR || t == SCHAR)) ||
+ (strg->st_tspec == WCHAR && t == WCHAR))) {
+ return (0);
+ }
+ /*
+ * If the array is already partly initialized, we are
+ * wrong here.
+ */
+ if (istk->i_cnt != istk->i_type->t_dim)
+ return (0);
+ } else {
+ return (0);
+ }
+
+ /* Get length without trailing NUL character. */
+ len = strg->st_len;
+
+ if (istk->i_nolimit) {
+ istk->i_nolimit = 0;
+ istk->i_type->t_dim = len + 1;
+ /* from now complete type */
+ setcompl(istk->i_type, 0);
+ } else {
+ if (istk->i_type->t_dim < len) {
+ /* non-null byte ignored in string initializer */
+ warning(187);
+ }
+ }
+
+ /* In every case the array is initialized completely. */
+ istk->i_cnt = 0;
+
+ return (1);
+}
diff --git a/usr.bin/xlint/lint1/lint.h b/usr.bin/xlint/lint1/lint.h
new file mode 100644
index 0000000..cadcd7a
--- /dev/null
+++ b/usr.bin/xlint/lint1/lint.h
@@ -0,0 +1,118 @@
+/* $NetBSD: lint.h,v 1.2 1995/07/03 21:24:18 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stddef.h>
+
+#include "param.h"
+
+/*
+ * Type specifiers, used in type structures (type_t) and otherwere.
+ */
+typedef enum {
+ NOTSPEC,
+ SIGNED, /* keyword "signed", only used in the parser */
+ UNSIGN, /* keyword "unsigned", only used in the parser */
+ CHAR, /* char */
+ SCHAR, /* signed char */
+ UCHAR, /* unsigned char */
+ SHORT, /* (signed) short */
+ USHORT, /* unsigned short */
+ INT, /* (signed) int */
+ UINT, /* unsigned int */
+ LONG, /* (signed) long */
+ ULONG, /* unsigned long */
+ QUAD, /* (signed) long long */
+ UQUAD, /* unsigned long long */
+ FLOAT, /* float */
+ DOUBLE, /* double or, with tflag, long float */
+ LDOUBLE, /* long double */
+ VOID, /* void */
+ STRUCT, /* structure tag */
+ UNION, /* union tag */
+ ENUM, /* enum tag */
+ PTR, /* pointer */
+ ARRAY, /* array */
+ FUNC /* function */
+#define NTSPEC ((int)FUNC + 1)
+} tspec_t;
+
+/*
+ * size of types, name and classification
+ */
+typedef struct {
+ int tt_sz; /* size in bits */
+ int tt_psz; /* size, different from tt_sz
+ if pflag is set */
+ tspec_t tt_styp; /* signed counterpart */
+ tspec_t tt_utyp; /* unsigned counterpart */
+ u_int tt_isityp : 1; /* 1 if integer type */
+ u_int tt_isutyp : 1; /* 1 if unsigned integer type */
+ u_int tt_isftyp : 1; /* 1 if floating point type */
+ u_int tt_isatyp : 1; /* 1 if arithmetic type */
+ u_int tt_issclt : 1; /* 1 if scalar type */
+ char *tt_name; /* Bezeichnung des Typs */
+} ttab_t;
+
+#define size(t) (ttab[t].tt_sz)
+#define psize(t) (ttab[t].tt_psz)
+#define styp(t) (ttab[t].tt_styp)
+#define utyp(t) (ttab[t].tt_utyp)
+#define isityp(t) (ttab[t].tt_isityp)
+#define isutyp(t) (ttab[t].tt_isutyp)
+#define isftyp(t) (ttab[t].tt_isftyp)
+#define isatyp(t) (ttab[t].tt_isatyp)
+#define issclt(t) (ttab[t].tt_issclt)
+
+extern ttab_t ttab[];
+
+
+typedef enum {
+ NODECL, /* until now not declared */
+ DECL, /* declared */
+ TDEF, /* tentative defined */
+ DEF /* defined */
+} def_t;
+
+/*
+ * Following structure contains some data used for the output buffer.
+ */
+typedef struct ob {
+ char *o_buf; /* buffer */
+ char *o_end; /* first byte after buffer */
+ size_t o_len; /* length of buffer */
+ char *o_nxt; /* next free byte in buffer */
+} ob_t;
+
+#include "externs.h"
diff --git a/usr.bin/xlint/lint1/lint1.h b/usr.bin/xlint/lint1/lint1.h
new file mode 100644
index 0000000..019c98d
--- /dev/null
+++ b/usr.bin/xlint/lint1/lint1.h
@@ -0,0 +1,380 @@
+/* $NetBSD: lint1.h,v 1.6 1995/10/02 17:31:41 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#include "lint.h"
+#include "op.h"
+
+/*
+ * Describes the position of a declaration or anything else.
+ */
+typedef struct {
+ int p_line;
+ const char *p_file;
+} pos_t;
+
+/*
+ * Strings cannot be referenced to simply by a pointer to its first
+ * char. This is because strings can contain NUL characters other than the
+ * trailing NUL.
+ *
+ * Strings are stored with a trailing NUL.
+ */
+typedef struct strg {
+ tspec_t st_tspec; /* CHAR or WCHAR */
+ size_t st_len; /* length without trailing NUL */
+ union {
+ u_char *_st_cp;
+ wchar_t *_st_wcp;
+ } st_u;
+} strg_t;
+
+#define st_cp st_u._st_cp
+#define st_wcp st_u._st_wcp
+
+/*
+ * qualifiers (only for lex/yacc interface)
+ */
+typedef enum {
+ CONST, VOLATILE
+} tqual_t;
+
+/*
+ * Integer and floating point values are stored in this structure
+ */
+typedef struct {
+ tspec_t v_tspec;
+ int v_ansiu; /* set if an integer constant is
+ unsigned in ANSI C */
+ union {
+ quad_t _v_quad; /* integers */
+ ldbl_t _v_ldbl; /* floats */
+ } v_u;
+} val_t;
+
+#define v_quad v_u._v_quad
+#define v_ldbl v_u._v_ldbl
+
+/*
+ * Structures of type str_t uniqely identify structures. This can't
+ * be done in structures of type type_t, because these are copied
+ * if they must be modified. So it would not be possible to check
+ * if to structures are identical by comparing the pointers to
+ * the type structures.
+ *
+ * The typename is used if the structure is unnamed to identify
+ * the structure type in pass 2.
+ */
+typedef struct {
+ u_int size; /* size in bit */
+ u_int align : 15; /* alignment in bit */
+ u_int sincompl : 1; /* set if incomplete type */
+ struct sym *memb; /* list of members */
+ struct sym *stag; /* symbol table entry of tag */
+ struct sym *stdef; /* symbol table entry of first typename */
+} str_t;
+
+/*
+ * same as above for enums
+ */
+typedef struct {
+ u_int eincompl : 1; /* incomplete enum type */
+ struct sym *elem; /* list of enumerators */
+ struct sym *etag; /* symbol table entry of tag */
+ struct sym *etdef; /* symbol table entry of first typename */
+} enum_t;
+
+/*
+ * Types are represented by concatenation of structures of type type_t
+ * via t_subt.
+ */
+typedef struct type {
+ tspec_t t_tspec; /* type specifier */
+ u_int t_aincompl : 1; /* incomplete array type */
+ u_int t_const : 1; /* const modifier */
+ u_int t_volatile : 1; /* volatile modifier */
+ u_int t_proto : 1; /* function prototype (t_args valid) */
+ u_int t_vararg : 1; /* protoype with ... */
+ u_int t_typedef : 1; /* type defined with typedef */
+ u_int t_isfield : 1; /* type is bitfield */
+ u_int t_isenum : 1; /* type is (or was) enum (t_enum valid) */
+ union {
+ int _t_dim; /* dimension */
+ str_t *_t_str; /* struct/union tag */
+ enum_t *_t_enum; /* enum tag */
+ struct sym *_t_args; /* arguments (if t_proto) */
+ struct {
+ u_int _t_flen : 8; /* length of bit-field */
+ u_int _t_foffs : 24; /* offset of bit-field */
+ } _t_u;
+ } t_u;
+ struct type *t_subt; /* element type (arrays), return value
+ (functions), or type pointer points to */
+} type_t;
+
+#define t_dim t_u._t_dim
+#define t_str t_u._t_str
+#define t_field t_u._t_field
+#define t_enum t_u._t_enum
+#define t_args t_u._t_args
+#define t_flen t_u._t_u._t_flen
+#define t_foffs t_u._t_u._t_foffs
+
+/*
+ * types of symbols
+ */
+typedef enum {
+ FVFT, /* variables, functions, type names, enums */
+ FMOS, /* members of structs or unions */
+ FTAG, /* tags */
+ FLAB /* labels */
+} symt_t;
+
+/*
+ * storage classes
+ */
+typedef enum {
+ NOSCL,
+ EXTERN, /* external symbols (indep. of decl_t) */
+ STATIC, /* static symbols (local and global) */
+ AUTO, /* automatic symbols (except register) */
+ REG, /* register */
+ TYPEDEF, /* typedef */
+ STRTAG,
+ UNIONTAG,
+ ENUMTAG,
+ MOS, /* member of struct */
+ MOU, /* member of union */
+ ENUMCON, /* enumerator */
+ ABSTRACT, /* abstract symbol (sizeof, casts, unnamed argument) */
+ ARG, /* argument */
+ PARG, /* used in declaration stack during prototype
+ declaration */
+ INLINE /* only used by the parser */
+} scl_t;
+
+/*
+ * symbol table entry
+ */
+typedef struct sym {
+ const char *s_name; /* name */
+ pos_t s_dpos; /* position of last (prototype)definition,
+ prototypedeclaration, no-prototype-def.,
+ tentative definition or declaration,
+ in this order */
+ pos_t s_spos; /* position of first initialisation */
+ pos_t s_upos; /* position of first use */
+ symt_t s_kind; /* type of symbol */
+ u_int s_keyw : 1; /* keyword */
+ u_int s_field : 1; /* bit-field */
+ u_int s_set : 1; /* variable set, label defined */
+ u_int s_used : 1; /* variable/label used */
+ u_int s_arg : 1; /* symbol is function argument */
+ u_int s_reg : 1; /* symbol is register variable */
+ u_int s_defarg : 1; /* undefined symbol in old style function
+ definition */
+ u_int s_rimpl : 1; /* return value of function implizit decl. */
+ u_int s_osdef : 1; /* symbol stems from old style function def. */
+ u_int s_inline : 1; /* true if this is a inline function */
+ struct sym *s_xsym; /* for local declared external symbols pointer
+ to external symbol with same name */
+ def_t s_def; /* declared, tentative defined, defined */
+ scl_t s_scl; /* storage class */
+ int s_blklev; /* level of declaration, -1 if not in symbol
+ table */
+ type_t *s_type; /* type */
+ val_t s_value; /* value (if enumcon) */
+ union {
+ str_t *_s_st; /* tag, if it is a struct/union member */
+ enum_t *_s_et; /* tag, if it is a enumerator */
+ tspec_t _s_tsp; /* type (only for keywords) */
+ tqual_t _s_tqu; /* qualifier (only for keywords) */
+ struct sym *_s_args; /* arguments in old style function
+ definitions */
+ } u;
+ struct sym *s_link; /* next symbol with same hash value */
+ struct sym **s_rlink; /* pointer to s_link of prev. symbol */
+ struct sym *s_nxt; /* next struct/union member, enumerator,
+ argument */
+ struct sym *s_dlnxt; /* next symbol declared on same level */
+} sym_t;
+
+#define s_styp u._s_st
+#define s_etyp u._s_et
+#define s_tspec u._s_tsp
+#define s_tqual u._s_tqu
+#define s_args u._s_args
+
+/*
+ * Used to keep some informations about symbols before they are entered
+ * into the symbol table.
+ */
+typedef struct sbuf {
+ const char *sb_name; /* name of symbol */
+ size_t sb_len; /* length (without '\0') */
+ int sb_hash; /* hash value */
+ sym_t *sb_sym; /* symbol table entry */
+ struct sbuf *sb_nxt; /* for freelist */
+} sbuf_t;
+
+
+/*
+ * tree node
+ */
+typedef struct tnode {
+ op_t tn_op; /* operator */
+ type_t *tn_type; /* type */
+ u_int tn_lvalue : 1; /* node is lvalue */
+ u_int tn_cast : 1; /* if tn_op == CVT its an explizit cast */
+ u_int tn_parn : 1; /* node parenthesized */
+ union {
+ struct {
+ struct tnode *_tn_left; /* (left) operand */
+ struct tnode *_tn_right; /* right operand */
+ } tn_s;
+ sym_t *_tn_sym; /* symbol if op == NAME */
+ val_t *_tn_val; /* value if op == CON */
+ strg_t *_tn_strg; /* string if op == STRING */
+ } tn_u;
+} tnode_t;
+
+#define tn_left tn_u.tn_s._tn_left
+#define tn_right tn_u.tn_s._tn_right
+#define tn_sym tn_u._tn_sym
+#define tn_val tn_u._tn_val
+#define tn_strg tn_u._tn_strg
+
+/*
+ * For nested declarations a stack exists, which holds all information
+ * needed for the current level. dcs points to the top element of this
+ * stack.
+ *
+ * ctx describes the context of the current declaration. Its value is
+ * one of
+ * EXTERN global declarations
+ * MOS oder MOU declarations of struct or union members
+ * ENUMCON declarations of enums
+ * ARG declaration of arguments in old style function definitions
+ * PARG declaration of arguments in function prototypes
+ * AUTO declaration of local symbols
+ * ABSTRACT abstract declarations (sizeof, casts)
+ *
+ */
+typedef struct dinfo {
+ tspec_t d_atyp; /* VOID, CHAR, INT, FLOAT or DOUBLE */
+ tspec_t d_smod; /* SIGNED or UNSIGN */
+ tspec_t d_lmod; /* SHORT, LONG or QUAD */
+ scl_t d_scl; /* storage class */
+ type_t *d_type; /* after deftyp() pointer to the type used
+ for all declarators */
+ sym_t *d_rdcsym; /* redeclared symbol */
+ int d_offset; /* offset of next structure member */
+ int d_stralign; /* alignment required for current structure */
+ scl_t d_ctx; /* context of declaration */
+ u_int d_const : 1; /* const in declaration specifiers */
+ u_int d_volatile : 1; /* volatile in declaration specifiers */
+ u_int d_inline : 1; /* inline in declaration specifiers */
+ u_int d_mscl : 1; /* multiple storage classes */
+ u_int d_terr : 1; /* invalid type combination */
+ u_int d_nedecl : 1; /* 1 if at least a tag is declared */
+ u_int d_vararg : 1; /* ... in in current function decl. */
+ u_int d_proto : 1; /* current funct. decl. is prototype */
+ u_int d_notyp : 1; /* set if no type specifier was present */
+ u_int d_asm : 1; /* set if d_ctx == AUTO and asm() present */
+ type_t *d_tagtyp; /* tag during member declaration */
+ sym_t *d_fargs; /* list of arguments during function def. */
+ pos_t d_fdpos; /* position of function definition */
+ sym_t *d_dlsyms; /* first symbol declared at this level */
+ sym_t **d_ldlsym; /* points to s_dlnxt in last symbol decl.
+ at this level */
+ sym_t *d_fpsyms; /* symbols defined in prototype */
+ struct dinfo *d_nxt; /* next level */
+} dinfo_t;
+
+/*
+ * Type of stack which is used for initialisation of aggregate types.
+ */
+typedef struct istk {
+ type_t *i_type; /* type of initialisation */
+ type_t *i_subt; /* type of next level */
+ u_int i_brace : 1; /* need } for pop */
+ u_int i_nolimit : 1; /* incomplete array type */
+ sym_t *i_mem; /* next structure member */
+ int i_cnt; /* # of remaining elements */
+ struct istk *i_nxt; /* previous level */
+} istk_t;
+
+/*
+ * Used to collect information about pointers and qualifiers in
+ * declarators.
+ */
+typedef struct pqinf {
+ int p_pcnt; /* number of asterisks */
+ u_int p_const : 1;
+ u_int p_volatile : 1;
+ struct pqinf *p_nxt;
+} pqinf_t;
+
+/*
+ * Case values are stored in a list of type clst_t.
+ */
+typedef struct clst {
+ val_t cl_val;
+ struct clst *cl_nxt;
+} clst_t;
+
+/*
+ * Used to keep informations about nested control statements.
+ */
+typedef struct cstk {
+ int c_env; /* type of statement (T_IF, ...) */
+ u_int c_loop : 1; /* continue && break are valid */
+ u_int c_switch : 1; /* case && break are valid */
+ u_int c_break : 1; /* loop/switch has break */
+ u_int c_cont : 1; /* loop has continue */
+ u_int c_default : 1; /* switch has default */
+ u_int c_infinite : 1; /* break condition always false
+ (for (;;), while (1)) */
+ u_int c_rchif : 1; /* end of if-branch reached */
+ u_int c_noretval : 1; /* had "return;" */
+ u_int c_retval : 1; /* had "return (e);" */
+ type_t *c_swtype; /* type of switch expression */
+ clst_t *c_clst; /* list of case values */
+ struct mbl *c_fexprm; /* saved memory for end of loop
+ expression in for() */
+ tnode_t *c_f3expr; /* end of loop expr in for() */
+ pos_t c_fpos; /* position of end of loop expr */
+ pos_t c_cfpos; /* same for csrc_pos */
+ struct cstk *c_nxt; /* outer control statement */
+} cstk_t;
+
+#include "externs1.h"
diff --git a/usr.bin/xlint/lint1/main1.c b/usr.bin/xlint/lint1/main1.c
new file mode 100644
index 0000000..0add206
--- /dev/null
+++ b/usr.bin/xlint/lint1/main1.c
@@ -0,0 +1,183 @@
+/* $NetBSD: main1.c,v 1.3 1995/10/02 17:29:56 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: main1.c,v 1.3 1995/10/02 17:29:56 jpo Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+
+#include "lint1.h"
+
+/* set yydebug to 1*/
+int yflag;
+
+/*
+ * Print warnings if an assignment of an integertype to another integertype
+ * causes an implizit narrowing conversion. If aflag is 1, these warnings
+ * are printed only if the source type is at least as wide as long. If aflag
+ * is greather then 1, they are always printed.
+ */
+int aflag;
+
+/* Print a warning if a break statement cannot be reached. */
+int bflag;
+
+/* Print warnings for pointer casts. */
+int cflag;
+
+/* Print various debug information. */
+int dflag;
+
+/* Perform stricter checking of enum types and operations on enum types. */
+int eflag;
+
+/* Print complete pathnames, not only the basename. */
+int Fflag;
+
+/* Enable some extensions of gcc */
+int gflag;
+
+/*
+ * Apply a number of heuristic tests to attempt to intuit bugs, improve
+ * style, and reduce waste.
+ */
+int hflag;
+
+/* Attempt to check portability to other dialects of C. */
+int pflag;
+
+/*
+ * In case of redeclarations/redefinitions print the location of the
+ * previous declaration/definition.
+ */
+int rflag;
+
+/* Strict ANSI C mode. */
+int sflag;
+
+/* Traditional C mode. */
+int tflag;
+
+/*
+ * Complain about functions and external variables used and not defined,
+ * or defined and not used.
+ */
+int uflag = 1;
+
+/* Complain about unused function arguments. */
+int vflag = 1;
+
+/* Complain about structures which are never defined. */
+int zflag = 1;
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "abcdeghprstuvyzF")) != -1) {
+ switch (c) {
+ case 'a': aflag++; break;
+ case 'b': bflag = 1; break;
+ case 'c': cflag = 1; break;
+ case 'd': dflag = 1; break;
+ case 'e': eflag = 1; break;
+ case 'F': Fflag = 1; break;
+ case 'g': gflag = 1; break;
+ case 'h': hflag = 1; break;
+ case 'p': pflag = 1; break;
+ case 'r': rflag = 1; break;
+ case 's': sflag = 1; break;
+ case 't': tflag = 1; break;
+ case 'u': uflag = 0; break;
+ case 'v': vflag = 0; break;
+ case 'y': yflag = 1; break;
+ case 'z': zflag = 0; break;
+ case '?': usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ /* open the input file */
+ if ((yyin = fopen(argv[0], "r")) == NULL)
+ err(1, "cannot open '%s'", argv[0]);
+
+ /* initialize output */
+ outopen(argv[1]);
+
+ if (yflag)
+ yydebug = 1;
+
+ initmem();
+ initdecl();
+ initscan();
+ initmtab();
+
+ yyparse();
+
+ /* Following warnings cannot be suppressed by LINTED */
+ nowarn = 0;
+
+ chkglsyms();
+
+ outclose();
+
+ return (nerr != 0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: lint1 [-abcdeghprstuvyzF] src dest\n");
+ exit(1);
+}
+
+void
+norecover()
+{
+ /* cannot recover from previous errors */
+ error(224);
+ exit(1);
+}
diff --git a/usr.bin/xlint/lint1/mem.c b/usr.bin/xlint/lint1/mem.c
new file mode 100644
index 0000000..71fba06
--- /dev/null
+++ b/usr.bin/xlint/lint1/mem.c
@@ -0,0 +1,91 @@
+/* $NetBSD: mem.c,v 1.2 1995/07/03 21:24:24 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: mem.c,v 1.2 1995/07/03 21:24:24 cgd Exp $";
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+
+#include "lint.h"
+
+void *
+xmalloc(s)
+ size_t s;
+{
+ void *p;
+
+ if ((p = malloc(s)) == NULL)
+ nomem();
+ return (p);
+}
+
+void *
+xcalloc(n, s)
+ size_t n, s;
+{
+ void *p;
+
+ if ((p = calloc(n, s)) == NULL)
+ nomem();
+ return (p);
+}
+
+void *
+xrealloc(p, s)
+ void *p;
+ size_t s;
+{
+ if ((p = realloc(p, s)) == NULL)
+ nomem();
+ return (p);
+}
+
+char *
+xstrdup(s)
+ const char *s;
+{
+ char *s2;
+
+ if ((s2 = strdup(s)) == NULL)
+ nomem();
+ return (s2);
+}
+
+void
+nomem()
+{
+ errx(1, "virtual memory exhausted");
+}
diff --git a/usr.bin/xlint/lint1/mem1.c b/usr.bin/xlint/lint1/mem1.c
new file mode 100644
index 0000000..f3bf3c6
--- /dev/null
+++ b/usr.bin/xlint/lint1/mem1.c
@@ -0,0 +1,358 @@
+/* $NetBSD: mem1.c,v 1.2 1995/07/03 21:24:25 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: mem1.c,v 1.2 1995/07/03 21:24:25 cgd Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+#include "lint1.h"
+
+/*
+ * Filenames allocated by fnalloc() and fnnalloc() are shared.
+ */
+typedef struct fn {
+ char *fn_name;
+ size_t fn_len;
+ int fn_id;
+ struct fn *fn_nxt;
+} fn_t;
+
+static fn_t *fnames;
+
+static fn_t *srchfn __P((const char *, size_t));
+
+/*
+ * Look for a Filename of length l.
+ */
+static fn_t *
+srchfn(s, len)
+ const char *s;
+ size_t len;
+{
+ fn_t *fn;
+
+ for (fn = fnames; fn != NULL; fn = fn->fn_nxt) {
+ if (fn->fn_len == len && memcmp(fn->fn_name, s, len) == 0)
+ break;
+ }
+ return (fn);
+}
+
+/*
+ * Return a shared string for filename s.
+ */
+const char *
+fnalloc(s)
+ const char *s;
+{
+ return (s != NULL ? fnnalloc(s, strlen(s)) : NULL);
+}
+
+const char *
+fnnalloc(s, len)
+ const char *s;
+ size_t len;
+{
+ fn_t *fn;
+
+ static int nxt_id = 0;
+
+ if (s == NULL)
+ return (NULL);
+
+ if ((fn = srchfn(s, len)) == NULL) {
+ fn = xmalloc(sizeof (fn_t));
+ /* Do not used strdup() because string is not NUL-terminated.*/
+ fn->fn_name = xmalloc(len + 1);
+ (void)memcpy(fn->fn_name, s, len);
+ fn->fn_name[len] = '\0';
+ fn->fn_len = len;
+ fn->fn_id = nxt_id++;
+ fn->fn_nxt = fnames;
+ fnames = fn;
+ /* Write id of this filename to the output file. */
+ outclr();
+ outint(fn->fn_id);
+ outchar('s');
+ outstrg(fn->fn_name);
+ }
+ return (fn->fn_name);
+}
+
+/*
+ * Get id of a filename.
+ */
+int
+getfnid(s)
+ const char *s;
+{
+ fn_t *fn;
+
+ if (s == NULL || (fn = srchfn(s, strlen(s))) == NULL)
+ return (-1);
+ return (fn->fn_id);
+}
+
+/*
+ * Memory for declarations and other things which must be available
+ * until the end of a block (or the end of the translation unit)
+ * are assoziated with the level (mblklev) of the block (or wiht 0).
+ * Because these memory is allocated in large blocks associated with
+ * a given level it can be freed easily at the end of a block.
+ */
+#define ML_INC ((size_t)32) /* Increment for length of *mblks */
+
+typedef struct mbl {
+ void *blk; /* beginning of memory block */
+ void *ffree; /* first free byte */
+ size_t nfree; /* # of free bytes */
+ size_t size; /* total size of memory block */
+ struct mbl *nxt; /* next block */
+} mbl_t;
+
+/*
+ * Array of pointers to lists of memory blocks. mblklev is used as
+ * index into this array.
+ */
+static mbl_t **mblks;
+
+/* number of elements in *mblks */
+static size_t nmblks;
+
+/* free list for memory blocks */
+static mbl_t *frmblks;
+
+/* length of new allocated memory blocks */
+static size_t mblklen;
+
+static void *xgetblk __P((mbl_t **, size_t));
+static void xfreeblk __P((mbl_t **));
+static mbl_t *xnewblk __P((void));
+
+static mbl_t *
+xnewblk()
+{
+ mbl_t *mb;
+ int prot, flags;
+
+ mb = xmalloc(sizeof (mbl_t));
+
+ /* use mmap instead of malloc to avoid malloc's size overhead */
+
+ prot = PROT_READ | PROT_WRITE;
+ flags = MAP_ANON | MAP_PRIVATE;
+ mb->blk = mmap(NULL, mblklen, prot, flags, -1, (off_t)0);
+ if (mb->blk == (void *)MAP_FAILED)
+ err(1, "can't map memory");
+ if (ALIGN((u_long)mb->blk) != (u_long)mb->blk)
+ errx(1, "mapped address is not aligned");
+
+ mb->size = mblklen;
+
+ return (mb);
+}
+
+/*
+ * Allocate new memory. If the first block of the list has not enough
+ * free space, or there is no first block, get a new block. The new
+ * block is taken from the free list or, if there is no block on the
+ * free list, is allocated using xnewblk(). If a new block is allocated
+ * it is initialized with zero. Blocks taken from the free list are
+ * zero'd in xfreeblk().
+ */
+static void *
+xgetblk(mbp, s)
+ mbl_t **mbp;
+ size_t s;
+{
+ mbl_t *mb;
+ void *p;
+
+ s = ALIGN(s);
+ if ((mb = *mbp) == NULL || mb->nfree < s) {
+ if ((mb = frmblks) == NULL) {
+ mb = xnewblk();
+ (void)memset(mb->blk, 0, mb->size);
+ } else {
+ frmblks = mb->nxt;
+ }
+ mb->ffree = mb->blk;
+ mb->nfree = mb->size;;
+ mb->nxt = *mbp;
+ *mbp = mb;
+ }
+ p = mb->ffree;
+ mb->ffree = (char *)mb->ffree + s;
+ mb->nfree -= s;
+ return (p);
+}
+
+/*
+ * Move all blocks from list *fmbp to free list. For each block, set all
+ * used memory to zero.
+ */
+static void
+xfreeblk(fmbp)
+ mbl_t **fmbp;
+{
+ mbl_t *mb;
+
+ while ((mb = *fmbp) != NULL) {
+ *fmbp = mb->nxt;
+ mb->nxt = frmblks;
+ frmblks = mb;
+ (void)memset(mb->blk, 0, mb->size - mb->nfree);
+ }
+}
+
+void
+initmem()
+{
+ int pgsz;
+
+ pgsz = getpagesize();
+ mblklen = ((MBLKSIZ + pgsz - 1) / pgsz) * pgsz;
+
+ mblks = xcalloc(nmblks = ML_INC, sizeof (mbl_t *));
+}
+
+
+/*
+ * Allocate memory associated with level l.
+ */
+void *
+getlblk(l, s)
+ int l;
+ size_t s;
+{
+ while (l >= nmblks) {
+ mblks = xrealloc(mblks, (nmblks + ML_INC) * sizeof (mbl_t *));
+ (void)memset(&mblks[nmblks], 0, ML_INC * sizeof (mbl_t *));
+ nmblks += ML_INC;
+ }
+ return (xgetblk(&mblks[l], s));
+}
+
+void *
+getblk(s)
+ size_t s;
+{
+ return (getlblk(mblklev, s));
+}
+
+/*
+ * Free all memory associated with level l.
+ */
+void
+freelblk(l)
+ int l;
+{
+ xfreeblk(&mblks[l]);
+}
+
+void
+freeblk()
+{
+ freelblk(mblklev);
+}
+
+/*
+ * tgetblk() returns memory which is associated with the current
+ * expression.
+ */
+static mbl_t *tmblk;
+
+void *
+tgetblk(s)
+ size_t s;
+{
+ return (xgetblk(&tmblk, s));
+}
+
+/*
+ * Get memory for a new tree node.
+ */
+tnode_t *
+getnode()
+{
+ return (tgetblk(sizeof (tnode_t)));
+}
+
+/*
+ * Free all memory which is allocated by the the current expression.
+ */
+void
+tfreeblk()
+{
+ xfreeblk(&tmblk);
+}
+
+/*
+ * Save the memory which is used by the current expression. This memory
+ * is not freed by the next tfreeblk() call. The pointer returned can be
+ * used to restore the memory.
+ */
+mbl_t *
+tsave()
+{
+ mbl_t *tmem;
+
+ tmem = tmblk;
+ tmblk = NULL;
+ return (tmem);
+}
+
+/*
+ * Free all memory used for the current expression and the memory used
+ * be a previous expression and saved by tsave(). The next call to
+ * tfreeblk() frees the restored memory.
+ */
+void
+trestor(tmem)
+ mbl_t *tmem;
+{
+ tfreeblk();
+ if (tmblk != NULL) {
+ free(tmblk->blk);
+ free(tmblk);
+ }
+ tmblk = tmem;
+}
diff --git a/usr.bin/xlint/lint1/op.h b/usr.bin/xlint/lint1/op.h
new file mode 100644
index 0000000..11f05a5
--- /dev/null
+++ b/usr.bin/xlint/lint1/op.h
@@ -0,0 +1,120 @@
+/* $NetBSD: op.h,v 1.2 1995/07/03 21:24:27 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+/*
+ * Various information about operators
+ */
+typedef struct {
+ u_int m_binary : 1; /* binary op. */
+ u_int m_logop : 1; /* logical op., result is int */
+ u_int m_rqint : 1; /* operands must have integer type */
+ u_int m_rqsclt : 1; /* operands must have scalar type */
+ u_int m_rqatyp : 1; /* operands must have arithmetic type */
+ u_int m_fold : 1; /* operands should be folded */
+ u_int m_vctx : 1; /* value context for left operand */
+ u_int m_tctx : 1; /* test context for left operand */
+ u_int m_balance : 1; /* op. requires balancing */
+ u_int m_sideeff : 1; /* op. has side effect */
+ u_int m_tlansiu : 1; /* warning if left op. is unsign. in ANSI C */
+ u_int m_transiu : 1; /* warning if right op. is unsign. in ANSI C */
+ u_int m_tpconf : 1; /* test possible precedence confusion */
+ u_int m_comp : 1; /* op. performs comparision */
+ u_int m_enumop : 1; /* valid operation on enums */
+ u_int m_badeop : 1; /* dubious operation on enums */
+ u_int m_eqwarn : 1; /* warning if on operand stems from == */
+ const char *m_name; /* name of op. */
+} mod_t;
+
+typedef enum {
+ NOOP = 0,
+ ARROW,
+ POINT,
+ NOT,
+ COMPL,
+ INC,
+ DEC,
+ INCBEF,
+ DECBEF,
+ INCAFT,
+ DECAFT,
+ UPLUS,
+ UMINUS,
+ STAR,
+ AMPER,
+ MULT,
+ DIV,
+ MOD,
+ PLUS,
+ MINUS,
+ SHL,
+ SHR,
+ LT,
+ LE,
+ GT,
+ GE,
+ EQ,
+ NE,
+ AND,
+ XOR,
+ OR,
+ LOGAND,
+ LOGOR,
+ QUEST,
+ COLON,
+ ASSIGN,
+ MULASS,
+ DIVASS,
+ MODASS,
+ ADDASS,
+ SUBASS,
+ SHLASS,
+ SHRASS,
+ ANDASS,
+ XORASS,
+ ORASS,
+ NAME,
+ CON,
+ STRING,
+ FSEL,
+ CALL,
+ COMMA,
+ CVT,
+ ICALL,
+ LOAD,
+ PUSH,
+ RETURN,
+ INIT, /* pseudo op, not used in trees */
+ CASE, /* pseudo op, not used in trees */
+ FARG /* pseudo op, not used in trees */
+#define NOPS ((int)FARG + 1)
+} op_t;
diff --git a/usr.bin/xlint/lint1/param.h b/usr.bin/xlint/lint1/param.h
new file mode 100644
index 0000000..17e266e
--- /dev/null
+++ b/usr.bin/xlint/lint1/param.h
@@ -0,0 +1,120 @@
+/* $NetBSD: param.h,v 1.4 1995/07/23 18:14:41 ragge Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+/*
+ * Minimun size of string buffer. If this is not enough, the buffer
+ * is enlarged in steps of STRBLEN bytes.
+ */
+#define STRBLEN 256
+
+/*
+ * This defines the size of memory blocks which are used to allocate
+ * memory in larger chunks.
+ */
+#define MBLKSIZ ((size_t)0x4000)
+
+/*
+ * Sizes of hash tables
+ * Should be a prime. Possible primes are
+ * 307, 401, 503, 601, 701, 809, 907, 1009, 1103, 1201, 1301, 1409, 1511.
+ *
+ * HSHSIZ1 symbol table 1st pass
+ * HSHSIZ2 symbol table 2nd pass
+ * THSHSIZ2 type table 2nd pass
+ */
+#define HSHSIZ1 503
+#define HSHSIZ2 1009
+#define THSHSIZ2 1009
+
+/*
+ * Should be set to 1 if the difference of two pointers is of type long
+ * or the value of sizeof is of type unsigned long.
+ */
+#ifdef __alpha__
+#define PTRDIFF_IS_LONG 1
+#define SIZEOF_IS_ULONG 1
+#elif __i386__
+#define PTRDIFF_IS_LONG 0
+#define SIZEOF_IS_ULONG 0
+#elif __m68k__
+#define PTRDIFF_IS_LONG 0
+#define SIZEOF_IS_ULONG 0
+#elif __ns32k__
+#define PTRDIFF_IS_LONG 0
+#define SIZEOF_IS_ULONG 0
+#elif __sparc__
+#define PTRDIFF_IS_LONG 0
+#define SIZEOF_IS_ULONG 0
+#elif __vax__
+#define PTRDIFF_IS_LONG 0
+#define SIZEOF_IS_ULONG 0
+#else
+#error unknown machine type
+#endif
+
+/*
+ * Make sure this matches wchar_t.
+ */
+#define WCHAR SHORT
+
+#ifndef __GNUC__
+#ifndef lint
+#ifndef QUAD_MAX /* necessary for mkdep */
+#define QUAD_MAX LONG_MAX
+#define QUAD_MIN LONG_MIN
+#define UQUAD_MAX ULONG_MAX
+#endif
+typedef long quad_t;
+typedef u_long u_quad_t;
+#endif
+#endif
+
+
+/*
+ * long double only in ANSI C.
+ */
+#ifdef __STDC__
+typedef long double ldbl_t;
+#else
+typedef double ldbl_t;
+#endif
+
+/*
+ * Some traditional compilers are not able to assign structures.
+ */
+#ifdef __STDC__
+#define STRUCT_ASSIGN(dest, src) (dest) = (src)
+#else
+#define STRUCT_ASSIGN(dest, src) (void)memcpy(&(dest), &(src), \
+ sizeof (dest));
+#endif
diff --git a/usr.bin/xlint/lint1/scan.l b/usr.bin/xlint/lint1/scan.l
new file mode 100644
index 0000000..ece6ef9
--- /dev/null
+++ b/usr.bin/xlint/lint1/scan.l
@@ -0,0 +1,1427 @@
+%{
+/* $NetBSD: scan.l,v 1.8 1995/10/23 13:38:51 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: scan.l,v 1.8 1995/10/23 13:38:51 jpo Exp $";
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <float.h>
+#include <ctype.h>
+#include <errno.h>
+#include <err.h>
+#include <math.h>
+
+#include "lint1.h"
+#include "y.tab.h"
+
+#define CHAR_MASK (~(~0 << CHAR_BIT))
+
+/* XXX declaration of strtouq() is missing in stdlib.h ? */
+extern u_quad_t strtouq __P((const char *, char **, int));
+
+/* Current position (its also updated when an included file is parsed) */
+pos_t curr_pos = { 1, "" };
+
+/*
+ * Current position in C source (not updated when an included file is
+ * parsed).
+ */
+pos_t csrc_pos = { 1, "" };
+
+static void incline __P((void));
+static void badchar __P((int));
+static sbuf_t *allocsb __P((void));
+static void freesb __P((sbuf_t *));
+static int inpc __P((void));
+static int hash __P((const char *));
+static sym_t *search __P((sbuf_t *));
+static int name __P((void));
+static int keyw __P((sym_t *));
+static int icon __P((int));
+static int fcon __P((void));
+static int operator __P((int, op_t));
+static int ccon __P((void));
+static int wccon __P((void));
+static int getescc __P((int));
+static void directive __P((void));
+static void comment __P((void));
+static int string __P((void));
+static int wcstrg __P((void));
+
+%}
+
+L [_A-Za-z]
+D [0-9]
+NZD [1-9]
+OD [0-7]
+HD [0-9A-Fa-f]
+EX ([eE][+-]?[0-9]+)
+
+%%
+
+{L}({L}|{D})* return (name());
+0{OD}*[lLuU]* return (icon(8));
+{NZD}{D}*[lLuU]* return (icon(10));
+0[xX]{HD}+[lLuU]* return (icon(16));
+{D}+\.{D}*{EX}?[fFlL]? |
+{D}+{EX}[fFlL]? |
+\.{D}+{EX}?[fFlL]? return (fcon());
+"=" return (operator(T_ASSIGN, ASSIGN));
+"*=" return (operator(T_OPASS, MULASS));
+"/=" return (operator(T_OPASS, DIVASS));
+"%=" return (operator(T_OPASS, MODASS));
+"+=" return (operator(T_OPASS, ADDASS));
+"-=" return (operator(T_OPASS, SUBASS));
+"<<=" return (operator(T_OPASS, SHLASS));
+">>=" return (operator(T_OPASS, SHRASS));
+"&=" return (operator(T_OPASS, ANDASS));
+"^=" return (operator(T_OPASS, XORASS));
+"|=" return (operator(T_OPASS, ORASS));
+"||" return (operator(T_LOGOR, LOGOR));
+"&&" return (operator(T_LOGAND, LOGAND));
+"|" return (operator(T_OR, OR));
+"&" return (operator(T_AND, AND));
+"^" return (operator(T_XOR, XOR));
+"==" return (operator(T_EQOP, EQ));
+"!=" return (operator(T_EQOP, NE));
+"<" return (operator(T_RELOP, LT));
+">" return (operator(T_RELOP, GT));
+"<=" return (operator(T_RELOP, LE));
+">=" return (operator(T_RELOP, GE));
+"<<" return (operator(T_SHFTOP, SHL));
+">>" return (operator(T_SHFTOP, SHR));
+"++" return (operator(T_INCDEC, INC));
+"--" return (operator(T_INCDEC, DEC));
+"->" return (operator(T_STROP, ARROW));
+"." return (operator(T_STROP, POINT));
+"+" return (operator(T_ADDOP, PLUS));
+"-" return (operator(T_ADDOP, MINUS));
+"*" return (operator(T_MULT, MULT));
+"/" return (operator(T_DIVOP, DIV));
+"%" return (operator(T_DIVOP, MOD));
+"!" return (operator(T_UNOP, NOT));
+"~" return (operator(T_UNOP, COMPL));
+"\"" return (string());
+"L\"" return (wcstrg());
+";" return (T_SEMI);
+"{" return (T_LBRACE);
+"}" return (T_RBRACE);
+"," return (T_COMMA);
+":" return (T_COLON);
+"?" return (T_QUEST);
+"[" return (T_LBRACK);
+"]" return (T_RBRACK);
+"(" return (T_LPARN);
+")" return (T_RPARN);
+"..." return (T_ELLIPSE);
+"'" return (ccon());
+"L'" return (wccon());
+^#.*$ directive();
+\n incline();
+\t|" "|\f|\v ;
+"/*" comment();
+. badchar(yytext[0]);
+
+%%
+
+static void
+incline()
+{
+ curr_pos.p_line++;
+ if (curr_pos.p_file == csrc_pos.p_file)
+ csrc_pos.p_line++;
+}
+
+static void
+badchar(c)
+ int c;
+{
+ /* unknown character \%o */
+ error(250, c);
+}
+
+/*
+ * Keywords.
+ * During initialisation they are written to the symbol table.
+ */
+static struct kwtab {
+ const char *kw_name; /* keyword */
+ int kw_token; /* token returned by yylex() */
+ scl_t kw_scl; /* storage class if kw_token T_SCLASS */
+ tspec_t kw_tspec; /* type spec. if kw_token T_TYPE or T_SOU */
+ tqual_t kw_tqual; /* type qual. fi kw_token T_QUAL */
+ u_int kw_stdc : 1; /* STDC keyword */
+ u_int kw_gcc : 1; /* GCC keyword */
+} kwtab[] = {
+ { "asm", T_ASM, 0, 0, 0, 0, 1 },
+ { "__asm", T_ASM, 0, 0, 0, 0, 0 },
+ { "__asm__", T_ASM, 0, 0, 0, 0, 0 },
+ { "auto", T_SCLASS, AUTO, 0, 0, 0, 0 },
+ { "break", T_BREAK, 0, 0, 0, 0, 0 },
+ { "case", T_CASE, 0, 0, 0, 0, 0 },
+ { "char", T_TYPE, 0, CHAR, 0, 0, 0 },
+ { "const", T_QUAL, 0, 0, CONST, 1, 0 },
+ { "__const__", T_QUAL, 0, 0, CONST, 0, 0 },
+ { "__const", T_QUAL, 0, 0, CONST, 0, 0 },
+ { "continue", T_CONTINUE, 0, 0, 0, 0, 0 },
+ { "default", T_DEFAULT, 0, 0, 0, 0, 0 },
+ { "do", T_DO, 0, 0, 0, 0, 0 },
+ { "double", T_TYPE, 0, DOUBLE, 0, 0, 0 },
+ { "else", T_ELSE, 0, 0, 0, 0, 0 },
+ { "enum", T_ENUM, 0, 0, 0, 0, 0 },
+ { "extern", T_SCLASS, EXTERN, 0, 0, 0, 0 },
+ { "float", T_TYPE, 0, FLOAT, 0, 0, 0 },
+ { "for", T_FOR, 0, 0, 0, 0, 0 },
+ { "goto", T_GOTO, 0, 0, 0, 0, 0 },
+ { "if", T_IF, 0, 0, 0, 0, 0 },
+ { "inline", T_SCLASS, INLINE, 0, 0, 0, 1 },
+ { "__inline__", T_SCLASS, INLINE, 0, 0, 0, 0 },
+ { "__inline", T_SCLASS, INLINE, 0, 0, 0, 0 },
+ { "int", T_TYPE, 0, INT, 0, 0, 0 },
+ { "long", T_TYPE, 0, LONG, 0, 0, 0 },
+ { "register", T_SCLASS, REG, 0, 0, 0, 0 },
+ { "return", T_RETURN, 0, 0, 0, 0, 0 },
+ { "short", T_TYPE, 0, SHORT, 0, 0, 0 },
+ { "signed", T_TYPE, 0, SIGNED, 0, 1, 0 },
+ { "__signed__", T_TYPE, 0, SIGNED, 0, 0, 0 },
+ { "__signed", T_TYPE, 0, SIGNED, 0, 0, 0 },
+ { "sizeof", T_SIZEOF, 0, 0, 0, 0, 0 },
+ { "static", T_SCLASS, STATIC, 0, 0, 0, 0 },
+ { "struct", T_SOU, 0, STRUCT, 0, 0, 0 },
+ { "switch", T_SWITCH, 0, 0, 0, 0, 0 },
+ { "typedef", T_SCLASS, TYPEDEF, 0, 0, 0, 0 },
+ { "union", T_SOU, 0, UNION, 0, 0, 0 },
+ { "unsigned", T_TYPE, 0, UNSIGN, 0, 0, 0 },
+ { "void", T_TYPE, 0, VOID, 0, 0, 0 },
+ { "volatile", T_QUAL, 0, 0, VOLATILE, 1, 0 },
+ { "__volatile__", T_QUAL, 0, 0, VOLATILE, 0, 0 },
+ { "__volatile", T_QUAL, 0, 0, VOLATILE, 0, 0 },
+ { "while", T_WHILE, 0, 0, 0, 0, 0 },
+ { NULL, 0, 0, 0, 0, 0, 0 }
+};
+
+/* Symbol table */
+static sym_t *symtab[HSHSIZ1];
+
+/* bit i of the entry with index i is set */
+u_quad_t qbmasks[sizeof(u_quad_t) * CHAR_BIT];
+
+/* least significant i bits are set in the entry with index i */
+u_quad_t qlmasks[sizeof(u_quad_t) * CHAR_BIT + 1];
+
+/* least significant i bits are not set in the entry with index i */
+u_quad_t qumasks[sizeof(u_quad_t) * CHAR_BIT + 1];
+
+/* free list for sbuf structures */
+static sbuf_t *sbfrlst;
+
+/* Typ of next expected symbol */
+symt_t symtyp;
+
+
+/*
+ * All keywords are written to the symbol table. This saves us looking
+ * in a extra table for each name we found.
+ */
+void
+initscan()
+{
+ struct kwtab *kw;
+ sym_t *sym;
+ int h, i;
+ u_quad_t uq;
+
+ for (kw = kwtab; kw->kw_name != NULL; kw++) {
+ if (kw->kw_stdc && tflag)
+ continue;
+ if (kw->kw_gcc && !gflag)
+ continue;
+ sym = getblk(sizeof (sym_t));
+ sym->s_name = kw->kw_name;
+ sym->s_keyw = 1;
+ sym->s_value.v_quad = kw->kw_token;
+ if (kw->kw_token == T_TYPE || kw->kw_token == T_SOU) {
+ sym->s_tspec = kw->kw_tspec;
+ } else if (kw->kw_token == T_SCLASS) {
+ sym->s_scl = kw->kw_scl;
+ } else if (kw->kw_token == T_QUAL) {
+ sym->s_tqual = kw->kw_tqual;
+ }
+ h = hash(sym->s_name);
+ if ((sym->s_link = symtab[h]) != NULL)
+ symtab[h]->s_rlink = &sym->s_link;
+ (symtab[h] = sym)->s_rlink = &symtab[h];
+ }
+
+ /* initialize bit-masks for quads */
+ for (i = 0; i < sizeof (u_quad_t) * CHAR_BIT; i++) {
+ qbmasks[i] = (u_quad_t)1 << i;
+ uq = ~(u_quad_t)0 << i;
+ qumasks[i] = uq;
+ qlmasks[i] = ~uq;
+ }
+ qumasks[i] = 0;
+ qlmasks[i] = ~(u_quad_t)0;
+}
+
+/*
+ * Get a free sbuf structure, if possible from the free list
+ */
+static sbuf_t *
+allocsb()
+{
+ sbuf_t *sb;
+
+ if ((sb = sbfrlst) != NULL) {
+ sbfrlst = sb->sb_nxt;
+ } else {
+ sb = xmalloc(sizeof (sbuf_t));
+ }
+ (void)memset(sb, 0, sizeof (sb));
+ return (sb);
+}
+
+/*
+ * Put a sbuf structure to the free list
+ */
+static void
+freesb(sb)
+ sbuf_t *sb;
+{
+ sb->sb_nxt = sbfrlst;
+ sbfrlst = sb;
+}
+
+/*
+ * Read a character and ensure that it is positive (except EOF).
+ * Increment line count(s) if necessary.
+ */
+static int
+inpc()
+{
+ int c;
+
+ if ((c = input()) != EOF && (c &= CHAR_MASK) == '\n')
+ incline();
+ return (c);
+}
+
+static int
+hash(s)
+ const char *s;
+{
+ u_int v;
+ const u_char *us;
+
+ v = 0;
+ for (us = (const u_char *)s; *us != '\0'; us++) {
+ v = (v << sizeof (v)) + *us;
+ v ^= v >> (sizeof (v) * CHAR_BIT - sizeof (v));
+ }
+ return (v % HSHSIZ1);
+}
+
+/*
+ * Lex has found a letter followed by zero or more letters or digits.
+ * It looks for a symbol in the symbol table with the same name. This
+ * symbol must either be a keyword or a symbol of the type required by
+ * symtyp (label, member, tag, ...).
+ *
+ * If it is a keyword, the token is returned. In some cases it is described
+ * more deeply by data written to yylval.
+ *
+ * If it is a symbol, T_NAME is returned and the pointer to a sbuf struct
+ * is stored in yylval. This struct contains the name of the symbol, it's
+ * length and hash value. If there is already a symbol of the same name
+ * and type in the symbol table, the sbuf struct also contains a pointer
+ * to the symbol table entry.
+ */
+static int
+name()
+{
+ char *s;
+ sbuf_t *sb;
+ sym_t *sym;
+ int tok;
+
+ sb = allocsb();
+ sb->sb_name = yytext;
+ sb->sb_len = yyleng;
+ sb->sb_hash = hash(yytext);
+
+ if ((sym = search(sb)) != NULL && sym->s_keyw) {
+ freesb(sb);
+ return (keyw(sym));
+ }
+
+ sb->sb_sym = sym;
+
+ if (sym != NULL) {
+ if (blklev < sym->s_blklev)
+ lerror("name() 1");
+ sb->sb_name = sym->s_name;
+ sb->sb_len = strlen(sym->s_name);
+ tok = sym->s_scl == TYPEDEF ? T_TYPENAME : T_NAME;
+ } else {
+ s = getblk(yyleng + 1);
+ (void)memcpy(s, yytext, yyleng + 1);
+ sb->sb_name = s;
+ sb->sb_len = yyleng;
+ tok = T_NAME;
+ }
+
+ yylval.y_sb = sb;
+ return (tok);
+}
+
+static sym_t *
+search(sb)
+ sbuf_t *sb;
+{
+ sym_t *sym;
+
+ for (sym = symtab[sb->sb_hash]; sym != NULL; sym = sym->s_link) {
+ if (strcmp(sym->s_name, sb->sb_name) == 0) {
+ if (sym->s_keyw || sym->s_kind == symtyp)
+ return (sym);
+ }
+ }
+
+ return (NULL);
+}
+
+static int
+keyw(sym)
+ sym_t *sym;
+{
+ int t;
+
+ if ((t = (int)sym->s_value.v_quad) == T_SCLASS) {
+ yylval.y_scl = sym->s_scl;
+ } else if (t == T_TYPE || t == T_SOU) {
+ yylval.y_tspec = sym->s_tspec;
+ } else if (t == T_QUAL) {
+ yylval.y_tqual = sym->s_tqual;
+ }
+ return (t);
+}
+
+/*
+ * Convert a string representing an integer into internal representation.
+ * The value is returned in yylval. icon() (and yylex()) returns T_CON.
+ */
+static int
+icon(base)
+ int base;
+{
+ int l_suffix, u_suffix;
+ int len;
+ const char *cp;
+ char c, *eptr;
+ tspec_t typ;
+ u_long ul;
+ u_quad_t uq;
+ int ansiu;
+ static tspec_t contypes[2][3] = {
+ { INT, LONG, QUAD },
+ { UINT, ULONG, UQUAD }
+ };
+
+ cp = yytext;
+ len = yyleng;
+
+ /* skip 0x */
+ if (base == 16) {
+ cp += 2;
+ len -= 2;
+ }
+
+ /* read suffixes */
+ l_suffix = u_suffix = 0;
+ for ( ; ; ) {
+ if ((c = cp[len - 1]) == 'l' || c == 'L') {
+ l_suffix++;
+ } else if (c == 'u' || c == 'U') {
+ u_suffix++;
+ } else {
+ break;
+ }
+ len--;
+ }
+ if (l_suffix > 2 || u_suffix > 1) {
+ /* malformed integer constant */
+ warning(251);
+ if (l_suffix > 2)
+ l_suffix = 2;
+ if (u_suffix > 1)
+ u_suffix = 1;
+ }
+ if (tflag && u_suffix != 0) {
+ /* suffix U is illegal in traditional C */
+ warning(97);
+ }
+ typ = contypes[u_suffix][l_suffix];
+
+ errno = 0;
+ if (l_suffix < 2) {
+ ul = strtoul(cp, &eptr, base);
+ } else {
+ uq = strtouq(cp, &eptr, base);
+ }
+ if (eptr != cp + len)
+ lerror("icon() 1");
+ if (errno != 0)
+ /* integer constant out of range */
+ warning(252);
+
+ /*
+ * If the value is to big for the current type, we must choose
+ * another type.
+ */
+ ansiu = 0;
+ switch (typ) {
+ case INT:
+ if (ul <= INT_MAX) {
+ /* ok */
+ } else if (ul <= (unsigned)UINT_MAX && base != 10) {
+ typ = UINT;
+ } else if (ul <= LONG_MAX) {
+ typ = LONG;
+ } else {
+ typ = ULONG;
+ }
+ if (typ == UINT || typ == ULONG) {
+ if (tflag) {
+ typ = LONG;
+ } else if (!sflag) {
+ /*
+ * Remember that the constant is unsigned
+ * only in ANSI C
+ */
+ ansiu = 1;
+ }
+ }
+ break;
+ case UINT:
+ if (ul > (u_int)UINT_MAX)
+ typ = ULONG;
+ break;
+ case LONG:
+ if (ul > LONG_MAX && !tflag) {
+ typ = ULONG;
+ if (!sflag)
+ ansiu = 1;
+ }
+ break;
+ case QUAD:
+ if (uq > QUAD_MAX && !tflag) {
+ typ = UQUAD;
+ if (!sflag)
+ ansiu = 1;
+ }
+ break;
+ /* LINTED (enumeration values not handled in switch) */
+ default:
+ }
+
+ if (typ != QUAD && typ != UQUAD) {
+ if (isutyp(typ)) {
+ uq = ul;
+ } else {
+ uq = (quad_t)(long)ul;
+ }
+ }
+
+ uq = (u_quad_t)xsign((quad_t)uq, typ, -1);
+
+ (yylval.y_val = xcalloc(1, sizeof (val_t)))->v_tspec = typ;
+ yylval.y_val->v_ansiu = ansiu;
+ yylval.y_val->v_quad = (quad_t)uq;
+
+ return (T_CON);
+}
+
+/*
+ * Returns 1 if t is a signed type and the value is negative.
+ *
+ * len is the number of significant bits. If len is -1, len is set
+ * to the width of type t.
+ */
+int
+sign(q, t, len)
+ quad_t q;
+ tspec_t t;
+ int len;
+{
+ if (t == PTR || isutyp(t))
+ return (0);
+ return (msb(q, t, len));
+}
+
+int
+msb(q, t, len)
+ quad_t q;
+ tspec_t t;
+ int len;
+{
+ if (len <= 0)
+ len = size(t);
+ return ((q & qbmasks[len - 1]) != 0);
+}
+
+/*
+ * Extends the sign of q.
+ */
+quad_t
+xsign(q, t, len)
+ quad_t q;
+ tspec_t t;
+ int len;
+{
+ if (len <= 0)
+ len = size(t);
+
+ if (t == PTR || isutyp(t) || !sign(q, t, len)) {
+ q &= qlmasks[len];
+ } else {
+ q |= qumasks[len];
+ }
+ return (q);
+}
+
+/*
+ * Convert a string representing a floating point value into its interal
+ * representation. Type and value are returned in yylval. fcon()
+ * (and yylex()) returns T_CON.
+ * XXX Currently it is not possible to convert constants of type
+ * long double which are greater then DBL_MAX.
+ */
+static int
+fcon()
+{
+ const char *cp;
+ int len;
+ tspec_t typ;
+ char c, *eptr;
+ double d;
+ float f;
+
+ cp = yytext;
+ len = yyleng;
+
+ if ((c = cp[len - 1]) == 'f' || c == 'F') {
+ typ = FLOAT;
+ len--;
+ } else if (c == 'l' || c == 'L') {
+ typ = LDOUBLE;
+ len--;
+ } else {
+ typ = DOUBLE;
+ }
+
+ if (tflag && typ != DOUBLE) {
+ /* suffixes F and L are illegal in traditional C */
+ warning(98);
+ }
+
+ errno = 0;
+ d = strtod(cp, &eptr);
+ if (eptr != cp + len)
+ lerror("fcon() 1");
+ if (errno != 0)
+ /* floating-point constant out of range */
+ warning(248);
+
+ if (typ == FLOAT) {
+ f = (float)d;
+ if (isinf(f)) {
+ /* floating-point constant out of range */
+ warning(248);
+ f = f > 0 ? FLT_MAX : -FLT_MAX;
+ }
+ }
+
+ (yylval.y_val = xcalloc(1, sizeof (val_t)))->v_tspec = typ;
+ if (typ == FLOAT) {
+ yylval.y_val->v_ldbl = f;
+ } else {
+ yylval.y_val->v_ldbl = d;
+ }
+
+ return (T_CON);
+}
+
+static int
+operator(t, o)
+ int t;
+ op_t o;
+{
+ yylval.y_op = o;
+ return (t);
+}
+
+/*
+ * Called if lex found a leading \'.
+ */
+static int
+ccon()
+{
+ int n, val, c;
+ char cv;
+
+ n = 0;
+ val = 0;
+ while ((c = getescc('\'')) >= 0) {
+ val = (val << CHAR_BIT) + c;
+ n++;
+ }
+ if (c == -2) {
+ /* unterminated character constant */
+ error(253);
+ } else {
+ if (n > sizeof (int) || (n > 1 && (pflag || hflag))) {
+ /* too many characters in character constant */
+ error(71);
+ } else if (n > 1) {
+ /* multi-character character constant */
+ warning(294);
+ } else if (n == 0) {
+ /* empty character constant */
+ error(73);
+ }
+ }
+ if (n == 1) {
+ cv = (char)val;
+ val = cv;
+ }
+
+ yylval.y_val = xcalloc(1, sizeof (val_t));
+ yylval.y_val->v_tspec = INT;
+ yylval.y_val->v_quad = val;
+
+ return (T_CON);
+}
+
+/*
+ * Called if lex found a leading L\'
+ */
+static int
+wccon()
+{
+ static char buf[MB_LEN_MAX + 1];
+ int i, c;
+ wchar_t wc;
+
+ i = 0;
+ while ((c = getescc('\'')) >= 0) {
+ if (i < MB_CUR_MAX)
+ buf[i] = (char)c;
+ i++;
+ }
+
+ wc = 0;
+
+ if (c == -2) {
+ /* unterminated character constant */
+ error(253);
+ } else if (c == 0) {
+ /* empty character constant */
+ error(73);
+ } else {
+ if (i > MB_CUR_MAX) {
+ i = MB_CUR_MAX;
+ /* too many characters in character constant */
+ error(71);
+ } else {
+ buf[i] = '\0';
+ (void)mbtowc(NULL, NULL, 0);
+ if (mbtowc(&wc, buf, MB_CUR_MAX) < 0)
+ /* invalid multibyte character */
+ error(291);
+ }
+ }
+
+ yylval.y_val = xcalloc(1, sizeof (val_t));
+ yylval.y_val->v_tspec = WCHAR;
+ yylval.y_val->v_quad = wc;
+
+ return (T_CON);
+}
+
+/*
+ * Read a character which is part of a character constant or of a string
+ * and handle escapes.
+ *
+ * The Argument is the character which delimits the character constant or
+ * string.
+ *
+ * Returns -1 if the end of the character constant or string is reached,
+ * -2 if the EOF is reached, and the charachter otherwise.
+ */
+static int
+getescc(d)
+ int d;
+{
+ static int pbc = -1;
+ int n, c, v;
+
+ if (pbc == -1) {
+ c = inpc();
+ } else {
+ c = pbc;
+ pbc = -1;
+ }
+ if (c == d)
+ return (-1);
+ switch (c) {
+ case '\n':
+ /* newline in string or char constant */
+ error(254);
+ return (-2);
+ case EOF:
+ return (-2);
+ case '\\':
+ switch (c = inpc()) {
+ case '"':
+ if (tflag && d == '\'')
+ /* \" inside character constant undef. ... */
+ warning(262);
+ return ('"');
+ case '\'':
+ return ('\'');
+ case '?':
+ if (tflag)
+ /* \? undefined in traditional C */
+ warning(263);
+ return ('?');
+ case '\\':
+ return ('\\');
+ case 'a':
+ if (tflag)
+ /* \a undefined in traditional C */
+ warning(81);
+#ifdef __STDC__
+ return ('\a');
+#else
+ return ('\007');
+#endif
+ case 'b':
+ return ('\b');
+ case 'f':
+ return ('\f');
+ case 'n':
+ return ('\n');
+ case 'r':
+ return ('\r');
+ case 't':
+ return ('\t');
+ case 'v':
+ if (tflag)
+ /* \v undefined in traditional C */
+ warning(264);
+#ifdef __STDC__
+ return ('\v');
+#else
+ return ('\013');
+#endif
+ case '8': case '9':
+ /* bad octal digit %c */
+ warning(77, c);
+ /* FALLTHROUGH */
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ n = 3;
+ v = 0;
+ do {
+ v = (v << 3) + (c - '0');
+ c = inpc();
+ } while (--n && isdigit(c) && (tflag || c <= '7'));
+ if (tflag && n > 0 && isdigit(c))
+ /* bad octal digit %c */
+ warning(77, c);
+ pbc = c;
+ if (v > UCHAR_MAX) {
+ /* character escape does not fit in char. */
+ warning(76);
+ v &= CHAR_MASK;
+ }
+ return (v);
+ case 'x':
+ if (tflag)
+ /* \x undefined in traditional C */
+ warning(82);
+ v = 0;
+ n = 0;
+ while ((c = inpc()) >= 0 && isxdigit(c)) {
+ c = isdigit(c) ?
+ c - '0' : toupper(c) - 'A' + 10;
+ v = (v << 4) + c;
+ if (n >= 0) {
+ if ((v & ~CHAR_MASK) != 0) {
+ /* overflow in hex escape */
+ warning(75);
+ n = -1;
+ } else {
+ n++;
+ }
+ }
+ }
+ pbc = c;
+ if (n == 0) {
+ /* no hex digits follow \x */
+ error(74);
+ } if (n == -1) {
+ v &= CHAR_MASK;
+ }
+ return (v);
+ case '\n':
+ return (getescc(d));
+ case EOF:
+ return (-2);
+ default:
+ if (isprint(c)) {
+ /* dubious escape \%c */
+ warning(79, c);
+ } else {
+ /* dubious escape \%o */
+ warning(80, c);
+ }
+ }
+ }
+ return (c);
+}
+
+/*
+ * Called for preprocessor directives. Currently implemented are:
+ * # lineno
+ * # lineno "filename"
+ */
+static void
+directive()
+{
+ const char *cp, *fn;
+ char c, *eptr;
+ size_t fnl;
+ long ln;
+ static int first = 1;
+
+ /* Go to first non-whitespace after # */
+ for (cp = yytext + 1; (c = *cp) == ' ' || c == '\t'; cp++) ;
+
+ if (!isdigit(c)) {
+ error:
+ /* undefined or invalid # directive */
+ warning(255);
+ return;
+ }
+ ln = strtol(--cp, &eptr, 10);
+ if (cp == eptr)
+ goto error;
+ if ((c = *(cp = eptr)) != ' ' && c != '\t' && c != '\0')
+ goto error;
+ while ((c = *cp++) == ' ' || c == '\t') ;
+ if (c != '\0') {
+ if (c != '"')
+ goto error;
+ fn = cp;
+ while ((c = *cp) != '"' && c != '\0')
+ cp++;
+ if (c != '"')
+ goto error;
+ if ((fnl = cp++ - fn) > PATH_MAX)
+ goto error;
+ while ((c = *cp++) == ' ' || c == '\t') ;
+#if 0
+ if (c != '\0')
+ warning("extra character(s) after directive");
+#endif
+ curr_pos.p_file = fnnalloc(fn, fnl);
+ /*
+ * If this is the first directive, the name is the name
+ * of the C source file as specified at the command line.
+ * It is written to the output file.
+ */
+ if (first) {
+ csrc_pos.p_file = curr_pos.p_file;
+ outsrc(curr_pos.p_file);
+ first = 0;
+ }
+ }
+ curr_pos.p_line = (int)ln - 1;
+ if (curr_pos.p_file == csrc_pos.p_file)
+ csrc_pos.p_line = (int)ln - 1;
+}
+
+/*
+ * Handle lint comments. Following comments are currently understood:
+ * ARGSUSEDn
+ * CONSTCOND CONSTANTCOND CONSTANTCONDITION
+ * FALLTHRU FALLTHROUGH
+ * LINTLIBRARY
+ * LINTED NOSTRICT
+ * LONGLONG
+ * NOTREACHED
+ * PRINTFLIKEn
+ * PROTOLIB
+ * SCANFLIKEn
+ * VARARGSn
+ * If one of this comments is recognized, the arguments, if any, are
+ * parsed and a function which handles this comment is called.
+ */
+static void
+comment()
+{
+ int c, lc;
+ static struct {
+ const char *keywd;
+ int arg;
+ void (*func) __P((int));
+ } keywtab[] = {
+ { "ARGSUSED", 1, argsused },
+ { "CONSTCOND", 0, constcond },
+ { "CONSTANTCOND", 0, constcond },
+ { "CONSTANTCONDITION", 0, constcond },
+ { "FALLTHRU", 0, fallthru },
+ { "FALLTHROUGH", 0, fallthru },
+ { "LINTLIBRARY", 0, lintlib },
+ { "LINTED", 0, linted },
+ { "LONGLONG", 0, longlong },
+ { "NOSTRICT", 0, linted },
+ { "NOTREACHED", 0, notreach },
+ { "PRINTFLIKE", 1, printflike },
+ { "PROTOLIB", 1, protolib },
+ { "SCANFLIKE", 1, scanflike },
+ { "VARARGS", 1, varargs },
+ };
+ char keywd[32];
+ char arg[32];
+ int l, i, a;
+ int eoc;
+
+ eoc = 0;
+
+ /* Skip white spaces after the start of the comment */
+ while ((c = inpc()) != EOF && isspace(c)) ;
+
+ /* Read the potential keyword to keywd */
+ l = 0;
+ while (c != EOF && isupper(c) && l < sizeof (keywd) - 1) {
+ keywd[l++] = (char)c;
+ c = inpc();
+ }
+ keywd[l] = '\0';
+
+ /* look for the keyword */
+ for (i = 0; i < sizeof (keywtab) / sizeof (keywtab[0]); i++) {
+ if (strcmp(keywtab[i].keywd, keywd) == 0)
+ break;
+ }
+ if (i == sizeof (keywtab) / sizeof (keywtab[0]))
+ goto skip_rest;
+
+ /* skip white spaces after the keyword */
+ while (c != EOF && isspace(c))
+ c = inpc();
+
+ /* read the argument, if the keyword accepts one and there is one */
+ l = 0;
+ if (keywtab[i].arg) {
+ while (c != EOF && isdigit(c) && l < sizeof (arg) - 1) {
+ arg[l++] = (char)c;
+ c = inpc();
+ }
+ }
+ arg[l] = '\0';
+ a = l != 0 ? atoi(arg) : -1;
+
+ /* skip white spaces after the argument */
+ while (c != EOF && isspace(c))
+ c = inpc();
+
+ if (c != '*' || (c = inpc()) != '/') {
+ if (keywtab[i].func != linted)
+ /* extra characters in lint comment */
+ warning(257);
+ } else {
+ /*
+ * remember that we have already found the end of the
+ * comment
+ */
+ eoc = 1;
+ }
+
+ if (keywtab[i].func != NULL)
+ (*keywtab[i].func)(a);
+
+ skip_rest:
+ while (!eoc) {
+ lc = c;
+ if ((c = inpc()) == EOF) {
+ /* unterminated comment */
+ error(256);
+ break;
+ }
+ if (lc == '*' && c == '/')
+ eoc = 1;
+ }
+}
+
+/*
+ * Clear flags for lint comments LINTED, LONGLONG and CONSTCOND.
+ * clrwflgs() is called after function definitions and global and
+ * local declarations and definitions. It is also called between
+ * the controlling expression and the body of control statements
+ * (if, switch, for, while).
+ */
+void
+clrwflgs()
+{
+ nowarn = 0;
+ quadflg = 0;
+ ccflg = 0;
+}
+
+/*
+ * Strings are stored in a dynamically alloceted buffer and passed
+ * in yylval.y_xstrg to the parser. The parser or the routines called
+ * by the parser are responsible for freeing this buffer.
+ */
+static int
+string()
+{
+ u_char *s;
+ int c;
+ size_t len, max;
+ strg_t *strg;
+
+ s = xmalloc(max = 64);
+
+ len = 0;
+ while ((c = getescc('"')) >= 0) {
+ /* +1 to reserve space for a trailing NUL character */
+ if (len + 1 == max)
+ s = xrealloc(s, max *= 2);
+ s[len++] = (char)c;
+ }
+ s[len] = '\0';
+ if (c == -2)
+ /* unterminated string constant */
+ error(258);
+
+ strg = xcalloc(1, sizeof (strg_t));
+ strg->st_tspec = CHAR;
+ strg->st_len = len;
+ strg->st_cp = s;
+
+ yylval.y_strg = strg;
+ return (T_STRING);
+}
+
+static int
+wcstrg()
+{
+ char *s;
+ int c, i, n, wi;
+ size_t len, max, wlen;
+ wchar_t *ws;
+ strg_t *strg;
+
+ s = xmalloc(max = 64);
+ len = 0;
+ while ((c = getescc('"')) >= 0) {
+ /* +1 to save space for a trailing NUL character */
+ if (len + 1 >= max)
+ s = xrealloc(s, max *= 2);
+ s[len++] = (char)c;
+ }
+ s[len] = '\0';
+ if (c == -2)
+ /* unterminated string constant */
+ error(258);
+
+ /* get length of wide character string */
+ (void)mblen(NULL, 0);
+ for (i = 0, wlen = 0; i < len; i += n, wlen++) {
+ if ((n = mblen(&s[i], MB_CUR_MAX)) == -1) {
+ /* invalid multibyte character */
+ error(291);
+ break;
+ }
+ if (n == 0)
+ n = 1;
+ }
+
+ ws = xmalloc((wlen + 1) * sizeof (wchar_t));
+
+ /* convert from multibyte to wide char */
+ (void)mbtowc(NULL, NULL, 0);
+ for (i = 0, wi = 0; i < len; i += n, wi++) {
+ if ((n = mbtowc(&ws[wi], &s[i], MB_CUR_MAX)) == -1)
+ break;
+ if (n == 0)
+ n = 1;
+ }
+ ws[wi] = 0;
+ free(s);
+
+ strg = xcalloc(1, sizeof (strg_t));
+ strg->st_tspec = WCHAR;
+ strg->st_len = wlen;
+ strg->st_wcp = ws;
+
+ yylval.y_strg = strg;
+ return (T_STRING);
+}
+
+/*
+ * As noted above the scanner does not create new symbol table entries
+ * for symbols it cannot find in the symbol table. This is to avoid
+ * putting undeclared symbols into the symbol table if a syntax error
+ * occurs.
+ *
+ * getsym() is called as soon as it is probably ok to put the symbol to
+ * the symbol table. This does not mean that it is not possible that
+ * symbols are put to the symbol table which are than not completely
+ * declared due to syntax errors. To avoid too many problems in this
+ * case symbols get type int in getsym().
+ *
+ * XXX calls to getsym() should be delayed until decl1*() is called
+ */
+sym_t *
+getsym(sb)
+ sbuf_t *sb;
+{
+ dinfo_t *di;
+ char *s;
+ sym_t *sym;
+
+ sym = sb->sb_sym;
+
+ /*
+ * During member declaration it is possible that name() looked
+ * for symbols of type FVFT, although it should have looked for
+ * symbols of type FTAG. Same can happen for labels. Both cases
+ * are compensated here.
+ */
+ if (symtyp == FMOS || symtyp == FLAB) {
+ if (sym == NULL || sym->s_kind == FVFT)
+ sym = search(sb);
+ }
+
+ if (sym != NULL) {
+ if (sym->s_kind != symtyp)
+ lerror("storesym() 1");
+ symtyp = FVFT;
+ freesb(sb);
+ return (sym);
+ }
+
+ /* create a new symbol table entry */
+
+ /* labels must always be allocated at level 1 (outhermost block) */
+ if (symtyp == FLAB) {
+ sym = getlblk(1, sizeof (sym_t));
+ s = getlblk(1, sb->sb_len + 1);
+ (void)memcpy(s, sb->sb_name, sb->sb_len + 1);
+ sym->s_name = s;
+ sym->s_blklev = 1;
+ di = dcs;
+ while (di->d_nxt != NULL && di->d_nxt->d_nxt != NULL)
+ di = di->d_nxt;
+ if (di->d_ctx != AUTO)
+ lerror("storesym() 2");
+ } else {
+ sym = getblk(sizeof (sym_t));
+ sym->s_name = sb->sb_name;
+ sym->s_blklev = blklev;
+ di = dcs;
+ }
+
+ STRUCT_ASSIGN(sym->s_dpos, curr_pos);
+ if ((sym->s_kind = symtyp) != FLAB)
+ sym->s_type = gettyp(INT);
+
+ symtyp = FVFT;
+
+ if ((sym->s_link = symtab[sb->sb_hash]) != NULL)
+ symtab[sb->sb_hash]->s_rlink = &sym->s_link;
+ (symtab[sb->sb_hash] = sym)->s_rlink = &symtab[sb->sb_hash];
+
+ *di->d_ldlsym = sym;
+ di->d_ldlsym = &sym->s_dlnxt;
+
+ freesb(sb);
+ return (sym);
+}
+
+/*
+ * Remove a symbol forever from the symbol table. s_blklev
+ * is set to -1 to avoid that the symbol will later be put
+ * back to the symbol table.
+ */
+void
+rmsym(sym)
+ sym_t *sym;
+{
+ if ((*sym->s_rlink = sym->s_link) != NULL)
+ sym->s_link->s_rlink = sym->s_rlink;
+ sym->s_blklev = -1;
+ sym->s_link = NULL;
+}
+
+/*
+ * Remove a list of symbols declared at one level from the symbol
+ * table.
+ */
+void
+rmsyms(syms)
+ sym_t *syms;
+{
+ sym_t *sym;
+
+ for (sym = syms; sym != NULL; sym = sym->s_dlnxt) {
+ if (sym->s_blklev != -1) {
+ if ((*sym->s_rlink = sym->s_link) != NULL)
+ sym->s_link->s_rlink = sym->s_rlink;
+ sym->s_link = NULL;
+ sym->s_rlink = NULL;
+ }
+ }
+}
+
+/*
+ * Put a symbol into the symbol table
+ */
+void
+inssym(bl, sym)
+ int bl;
+ sym_t *sym;
+{
+ int h;
+
+ h = hash(sym->s_name);
+ if ((sym->s_link = symtab[h]) != NULL)
+ symtab[h]->s_rlink = &sym->s_link;
+ (symtab[h] = sym)->s_rlink = &symtab[h];
+ sym->s_blklev = bl;
+ if (sym->s_link != NULL && sym->s_blklev < sym->s_link->s_blklev)
+ lerror("inssym()");
+}
+
+/*
+ * Called at level 0 after syntax errors
+ * Removes all symbols which are not declared at level 0 from the
+ * symbol table. Also frees all memory which is not associated with
+ * level 0.
+ */
+void
+cleanup()
+{
+ sym_t *sym, *nsym;
+ int i;
+
+ for (i = 0; i < HSHSIZ1; i++) {
+ for (sym = symtab[i]; sym != NULL; sym = nsym) {
+ nsym = sym->s_link;
+ if (sym->s_blklev >= 1) {
+ if ((*sym->s_rlink = nsym) != NULL)
+ nsym->s_rlink = sym->s_rlink;
+ }
+ }
+ }
+
+ for (i = mblklev; i > 0; i--)
+ freelblk(i);
+}
+
+/*
+ * Create a new symbol with the name of an existing symbol.
+ */
+sym_t *
+pushdown(sym)
+ sym_t *sym;
+{
+ int h;
+ sym_t *nsym;
+
+ h = hash(sym->s_name);
+ nsym = getblk(sizeof (sym_t));
+ if (sym->s_blklev > blklev)
+ lerror("pushdown()");
+ nsym->s_name = sym->s_name;
+ STRUCT_ASSIGN(nsym->s_dpos, curr_pos);
+ nsym->s_kind = sym->s_kind;
+ nsym->s_blklev = blklev;
+
+ if ((nsym->s_link = symtab[h]) != NULL)
+ symtab[h]->s_rlink = &nsym->s_link;
+ (symtab[h] = nsym)->s_rlink = &symtab[h];
+
+ *dcs->d_ldlsym = nsym;
+ dcs->d_ldlsym = &nsym->s_dlnxt;
+
+ return (nsym);
+}
+
+/*
+ * Free any dynamically allocated memory referenced by
+ * the value stack or yylval.
+ * The type of information in yylval is described by tok.
+ */
+void
+freeyyv(sp, tok)
+ void *sp;
+ int tok;
+{
+ if (tok == T_NAME || tok == T_TYPENAME) {
+ sbuf_t *sb = *(sbuf_t **)sp;
+ freesb(sb);
+ } else if (tok == T_CON) {
+ val_t *val = *(val_t **)sp;
+ free(val);
+ } else if (tok == T_STRING) {
+ strg_t *strg = *(strg_t **)sp;
+ if (strg->st_tspec == CHAR) {
+ free(strg->st_cp);
+ } else if (strg->st_tspec == WCHAR) {
+ free(strg->st_wcp);
+ } else {
+ lerror("fryylv() 1");
+ }
+ free(strg);
+ }
+}
diff --git a/usr.bin/xlint/lint1/tree.c b/usr.bin/xlint/lint1/tree.c
new file mode 100644
index 0000000..5770f08
--- /dev/null
+++ b/usr.bin/xlint/lint1/tree.c
@@ -0,0 +1,3927 @@
+/* $NetBSD: tree.c,v 1.12 1995/10/02 17:37:57 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: tree.c,v 1.12 1995/10/02 17:37:57 jpo Exp $";
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+
+#include "lint1.h"
+#include "y.tab.h"
+
+/* Various flags for each operator. */
+static mod_t modtab[NOPS];
+
+static tnode_t *getinode __P((tspec_t, quad_t));
+static void ptrcmpok __P((op_t, tnode_t *, tnode_t *));
+static int asgntypok __P((op_t, int, tnode_t *, tnode_t *));
+static void chkbeop __P((op_t, tnode_t *, tnode_t *));
+static void chkeop2 __P((op_t, int, tnode_t *, tnode_t *));
+static void chkeop1 __P((op_t, int, tnode_t *, tnode_t *));
+static tnode_t *mktnode __P((op_t, type_t *, tnode_t *, tnode_t *));
+static void balance __P((op_t, tnode_t **, tnode_t **));
+static void incompat __P((op_t, tspec_t, tspec_t));
+static void illptrc __P((mod_t *, type_t *, type_t *));
+static void mrgqual __P((type_t **, type_t *, type_t *));
+static int conmemb __P((type_t *));
+static void ptconv __P((int, tspec_t, tspec_t, type_t *, tnode_t *));
+static void iiconv __P((op_t, int, tspec_t, tspec_t, type_t *, tnode_t *));
+static void piconv __P((op_t, tspec_t, type_t *, tnode_t *));
+static void ppconv __P((op_t, tnode_t *, type_t *));
+static tnode_t *bldstr __P((op_t, tnode_t *, tnode_t *));
+static tnode_t *bldincdec __P((op_t, tnode_t *));
+static tnode_t *bldamper __P((tnode_t *, int));
+static tnode_t *bldplmi __P((op_t, tnode_t *, tnode_t *));
+static tnode_t *bldshft __P((op_t, tnode_t *, tnode_t *));
+static tnode_t *bldcol __P((tnode_t *, tnode_t *));
+static tnode_t *bldasgn __P((op_t, tnode_t *, tnode_t *));
+static tnode_t *plength __P((type_t *));
+static tnode_t *fold __P((tnode_t *));
+static tnode_t *foldtst __P((tnode_t *));
+static tnode_t *foldflt __P((tnode_t *));
+static tnode_t *chkfarg __P((type_t *, tnode_t *));
+static tnode_t *parg __P((int, type_t *, tnode_t *));
+static void nulleff __P((tnode_t *));
+static void displexpr __P((tnode_t *, int));
+static void chkaidx __P((tnode_t *, int));
+static void chkcomp __P((op_t, tnode_t *, tnode_t *));
+static void precconf __P((tnode_t *));
+
+/*
+ * Initialize mods of operators.
+ */
+void
+initmtab()
+{
+ static struct {
+ op_t op;
+ mod_t m;
+ } imods[] = {
+ { ARROW, { 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,
+ "->" } },
+ { POINT, { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ "." } },
+ { NOT, { 0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,
+ "!" } },
+ { COMPL, { 0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,1,1,
+ "~" } },
+ { INCBEF, { 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,
+ "prefix++" } },
+ { DECBEF, { 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,
+ "prefix--" } },
+ { INCAFT, { 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,
+ "postfix++" } },
+ { DECAFT, { 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,
+ "postfix--" } },
+ { UPLUS, { 0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,1,
+ "unary +" } },
+ { UMINUS, { 0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,1,1,
+ "unary -" } },
+ { STAR, { 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,
+ "unary *" } },
+ { AMPER, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ "unary &" } },
+ { MULT, { 1,0,0,0,1,1,1,0,1,0,0,1,0,0,0,1,1,
+ "*" } },
+ { DIV, { 1,0,0,0,1,1,1,0,1,0,1,1,0,0,0,1,1,
+ "/" } },
+ { MOD, { 1,0,1,0,0,1,1,0,1,0,1,1,0,0,0,1,1,
+ "%" } },
+ { PLUS, { 1,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0,
+ "+" } },
+ { MINUS, { 1,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0,
+ "-" } },
+ { SHL, { 1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,1,1,
+ "<<" } },
+ { SHR, { 1,0,1,0,0,1,1,0,0,0,1,0,1,0,0,1,1,
+ ">>" } },
+ { LT, { 1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1,
+ "<" } },
+ { LE, { 1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1,
+ "<=" } },
+ { GT, { 1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1,
+ ">" } },
+ { GE, { 1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1,
+ ">=" } },
+ { EQ, { 1,1,0,1,0,1,1,0,1,0,0,0,0,1,1,0,1,
+ "==" } },
+ { NE, { 1,1,0,1,0,1,1,0,1,0,0,0,0,1,1,0,1,
+ "!=" } },
+ { AND, { 1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,
+ "&" } },
+ { XOR, { 1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,
+ "^" } },
+ { OR, { 1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,
+ "|" } },
+ { LOGAND, { 1,1,0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,
+ "&&" } },
+ { LOGOR, { 1,1,0,1,0,1,0,1,0,0,0,0,1,0,0,1,0,
+ "||" } },
+ { QUEST, { 1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,
+ "?" } },
+ { COLON, { 1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,
+ ":" } },
+ { ASSIGN, { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,
+ "=" } },
+ { MULASS, { 1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,
+ "*=" } },
+ { DIVASS, { 1,0,0,0,1,0,0,0,0,1,0,1,0,0,0,1,0,
+ "/=" } },
+ { MODASS, { 1,0,1,0,0,0,0,0,0,1,0,1,0,0,0,1,0,
+ "%=" } },
+ { ADDASS, { 1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,
+ "+=" } },
+ { SUBASS, { 1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,
+ "-=" } },
+ { SHLASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,
+ "<<=" } },
+ { SHRASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,
+ ">>=" } },
+ { ANDASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,
+ "&=" } },
+ { XORASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,
+ "^=" } },
+ { ORASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,
+ "|=" } },
+ { NAME, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ "NAME" } },
+ { CON, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ "CON" } },
+ { STRING, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ "STRING" } },
+ { FSEL, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ "FSEL" } },
+ { CALL, { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+ "CALL" } },
+ { COMMA, { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ "," } },
+ { CVT, { 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,
+ "CVT" } },
+ { ICALL, { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
+ "ICALL" } },
+ { LOAD, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ "LOAD" } },
+ { PUSH, { 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,
+ "PUSH" } },
+ { RETURN, { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,
+ "RETURN" } },
+ { INIT, { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,
+ "INIT" } },
+ { FARG, { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,
+ "FARG" } },
+ { NOOP }
+ };
+ int i;
+
+ for (i = 0; imods[i].op != NOOP; i++)
+ STRUCT_ASSIGN(modtab[imods[i].op], imods[i].m);
+}
+
+/*
+ * Increase degree of reference.
+ * This is most often used to change type "T" in type "pointer to T".
+ */
+type_t *
+incref(tp, t)
+ type_t *tp;
+ tspec_t t;
+{
+ type_t *tp2;
+
+ tp2 = getblk(sizeof (type_t));
+ tp2->t_tspec = t;
+ tp2->t_subt = tp;
+ return (tp2);
+}
+
+/*
+ * same for use in expressions
+ */
+type_t *
+tincref(tp, t)
+ type_t *tp;
+ tspec_t t;
+{
+ type_t *tp2;
+
+ tp2 = tgetblk(sizeof (type_t));
+ tp2->t_tspec = t;
+ tp2->t_subt = tp;
+ return (tp2);
+}
+
+/*
+ * Create a node for a constant.
+ */
+tnode_t *
+getcnode(tp, v)
+ type_t *tp;
+ val_t *v;
+{
+ tnode_t *n;
+
+ n = getnode();
+ n->tn_op = CON;
+ n->tn_type = tp;
+ n->tn_val = tgetblk(sizeof (val_t));
+ n->tn_val->v_tspec = tp->t_tspec;
+ n->tn_val->v_ansiu = v->v_ansiu;
+ n->tn_val->v_u = v->v_u;
+ free(v);
+ return (n);
+}
+
+/*
+ * Create a node for a integer constant.
+ */
+static tnode_t *
+getinode(t, q)
+ tspec_t t;
+ quad_t q;
+{
+ tnode_t *n;
+
+ n = getnode();
+ n->tn_op = CON;
+ n->tn_type = gettyp(t);
+ n->tn_val = tgetblk(sizeof (val_t));
+ n->tn_val->v_tspec = t;
+ n->tn_val->v_quad = q;
+ return (n);
+}
+
+/*
+ * Create a node for a name (symbol table entry).
+ * ntok is the token which follows the name.
+ */
+tnode_t *
+getnnode(sym, ntok)
+ sym_t *sym;
+ int ntok;
+{
+ tnode_t *n;
+
+ if (sym->s_scl == NOSCL) {
+ sym->s_scl = EXTERN;
+ sym->s_def = DECL;
+ if (ntok == T_LPARN) {
+ if (sflag) {
+ /* function implicitly declared to ... */
+ warning(215);
+ }
+ /*
+ * XXX if tflag is set the symbol should be
+ * exported to level 0
+ */
+ sym->s_type = incref(sym->s_type, FUNC);
+ } else {
+ /* %s undefined */
+ error(99, sym->s_name);
+ }
+ }
+
+ if (sym->s_kind != FVFT && sym->s_kind != FMOS)
+ lerror("getnnode() 1");
+
+ n = getnode();
+ n->tn_type = sym->s_type;
+ if (sym->s_scl != ENUMCON) {
+ n->tn_op = NAME;
+ n->tn_sym = sym;
+ if (sym->s_kind == FVFT && sym->s_type->t_tspec != FUNC)
+ n->tn_lvalue = 1;
+ } else {
+ n->tn_op = CON;
+ n->tn_val = tgetblk(sizeof (val_t));
+ *n->tn_val = sym->s_value;
+ }
+
+ return (n);
+}
+
+/*
+ * Create a node for a string.
+ */
+tnode_t *
+getsnode(strg)
+ strg_t *strg;
+{
+ size_t len;
+ tnode_t *n;
+
+ len = strg->st_len;
+
+ n = getnode();
+
+ n->tn_op = STRING;
+ n->tn_type = tincref(gettyp(strg->st_tspec), ARRAY);
+ n->tn_type->t_dim = len + 1;
+ n->tn_lvalue = 1;
+
+ n->tn_strg = tgetblk(sizeof (strg_t));
+ n->tn_strg->st_tspec = strg->st_tspec;
+ n->tn_strg->st_len = len;
+
+ if (strg->st_tspec == CHAR) {
+ n->tn_strg->st_cp = tgetblk(len + 1);
+ (void)memcpy(n->tn_strg->st_cp, strg->st_cp, len + 1);
+ free(strg->st_cp);
+ } else {
+ n->tn_strg->st_wcp = tgetblk((len + 1) * sizeof (wchar_t));
+ (void)memcpy(n->tn_strg->st_wcp, strg->st_wcp,
+ (len + 1) * sizeof (wchar_t));
+ free(strg->st_wcp);
+ }
+ free(strg);
+
+ return (n);
+}
+
+/*
+ * Returns a symbol which has the same name as the msym argument and is a
+ * member of the struct or union specified by the tn argument.
+ */
+sym_t *
+strmemb(tn, op, msym)
+ tnode_t *tn;
+ op_t op;
+ sym_t *msym;
+{
+ str_t *str;
+ type_t *tp;
+ sym_t *sym, *csym;
+ int eq;
+ tspec_t t;
+
+ /*
+ * Remove the member if it was unknown until now (Which means
+ * that no defined struct or union has a member with the same name).
+ */
+ if (msym->s_scl == NOSCL) {
+ /* undefined struct/union member: %s */
+ error(101, msym->s_name);
+ rmsym(msym);
+ msym->s_kind = FMOS;
+ msym->s_scl = MOS;
+ msym->s_styp = tgetblk(sizeof (str_t));
+ msym->s_styp->stag = tgetblk(sizeof (sym_t));
+ msym->s_styp->stag->s_name = unnamed;
+ msym->s_value.v_tspec = INT;
+ return (msym);
+ }
+
+ /* Set str to the tag of which msym is expected to be a member. */
+ str = NULL;
+ t = (tp = tn->tn_type)->t_tspec;
+ if (op == POINT) {
+ if (t == STRUCT || t == UNION)
+ str = tp->t_str;
+ } else if (op == ARROW && t == PTR) {
+ t = (tp = tp->t_subt)->t_tspec;
+ if (t == STRUCT || t == UNION)
+ str = tp->t_str;
+ }
+
+ /*
+ * If this struct/union has a member with the name of msym, return
+ * return this it.
+ */
+ if (str != NULL) {
+ for (sym = msym; sym != NULL; sym = sym->s_link) {
+ if (sym->s_scl != MOS && sym->s_scl != MOU)
+ continue;
+ if (sym->s_styp != str)
+ continue;
+ if (strcmp(sym->s_name, msym->s_name) != 0)
+ continue;
+ return (sym);
+ }
+ }
+
+ /*
+ * Set eq to 0 if there are struct/union members with the same name
+ * and different types and/or offsets.
+ */
+ eq = 1;
+ for (csym = msym; csym != NULL; csym = csym->s_link) {
+ if (csym->s_scl != MOS && csym->s_scl != MOU)
+ continue;
+ if (strcmp(msym->s_name, csym->s_name) != 0)
+ continue;
+ for (sym = csym->s_link ; sym != NULL; sym = sym->s_link) {
+ int w;
+
+ if (sym->s_scl != MOS && sym->s_scl != MOU)
+ continue;
+ if (strcmp(csym->s_name, sym->s_name) != 0)
+ continue;
+ if (csym->s_value.v_quad != sym->s_value.v_quad) {
+ eq = 0;
+ break;
+ }
+ w = 0;
+ eq = eqtype(csym->s_type, sym->s_type, 0, 0, &w) && !w;
+ if (!eq)
+ break;
+ if (csym->s_field != sym->s_field) {
+ eq = 0;
+ break;
+ }
+ if (csym->s_field) {
+ type_t *tp1, *tp2;
+
+ tp1 = csym->s_type;
+ tp2 = sym->s_type;
+ if (tp1->t_flen != tp2->t_flen) {
+ eq = 0;
+ break;
+ }
+ if (tp1->t_foffs != tp2->t_foffs) {
+ eq = 0;
+ break;
+ }
+ }
+ }
+ if (!eq)
+ break;
+ }
+
+ /*
+ * Now handle the case in which the left operand refers really
+ * to a struct/union, but the right operand is not member of it.
+ */
+ if (str != NULL) {
+ /* illegal member use: %s */
+ if (eq && tflag) {
+ warning(102, msym->s_name);
+ } else {
+ error(102, msym->s_name);
+ }
+ return (msym);
+ }
+
+ /*
+ * Now the left operand of ARROW does not point to a struct/union
+ * or the left operand of POINT is no struct/union.
+ */
+ if (eq) {
+ if (op == POINT) {
+ /* left operand of "." must be struct/union object */
+ if (tflag) {
+ warning(103);
+ } else {
+ error(103);
+ }
+ } else {
+ /* left operand of "->" must be pointer to ... */
+ if (tflag && tn->tn_type->t_tspec == PTR) {
+ warning(104);
+ } else {
+ error(104);
+ }
+ }
+ } else {
+ if (tflag) {
+ /* non-unique member requires struct/union %s */
+ error(105, op == POINT ? "object" : "pointer");
+ } else {
+ /* unacceptable operand of %s */
+ error(111, modtab[op].m_name);
+ }
+ }
+
+ return (msym);
+}
+
+/*
+ * Create a tree node. Called for most operands except function calls,
+ * sizeof and casts.
+ *
+ * op operator
+ * ln left operand
+ * rn if not NULL, right operand
+ */
+tnode_t *
+build(op, ln, rn)
+ op_t op;
+ tnode_t *ln, *rn;
+{
+ mod_t *mp;
+ tnode_t *ntn;
+ type_t *rtp;
+
+ mp = &modtab[op];
+
+ /* If there was an error in one of the operands, return. */
+ if (ln == NULL || (mp->m_binary && rn == NULL))
+ return (NULL);
+
+ /*
+ * Apply class conversions to the left operand, but only if its
+ * value is needed or it is compaired with null.
+ */
+ if (mp->m_vctx || mp->m_tctx)
+ ln = cconv(ln);
+ /*
+ * The right operand is almost always in a test or value context,
+ * except if it is a struct or union member.
+ */
+ if (mp->m_binary && op != ARROW && op != POINT)
+ rn = cconv(rn);
+
+ /*
+ * Print some warnings for comparisions of unsigned values with
+ * constants lower than or equal to null. This must be done
+ * before promote() because otherwise unsigned char and unsigned
+ * short would be promoted to int. Also types are tested to be
+ * CHAR, which would also become int.
+ */
+ if (mp->m_comp)
+ chkcomp(op, ln, rn);
+
+ /*
+ * Promote the left operand if it is in a test or value context
+ */
+ if (mp->m_vctx || mp->m_tctx)
+ ln = promote(op, 0, ln);
+ /*
+ * Promote the right operand, but only if it is no struct or
+ * union member, or if it is not to be assigned to the left operand
+ */
+ if (mp->m_binary && op != ARROW && op != POINT &&
+ op != ASSIGN && op != RETURN) {
+ rn = promote(op, 0, rn);
+ }
+
+ /*
+ * If the result of the operation is different for signed or
+ * unsigned operands and one of the operands is signed only in
+ * ANSI C, print a warning.
+ */
+ if (mp->m_tlansiu && ln->tn_op == CON && ln->tn_val->v_ansiu) {
+ /* ANSI C treats constant as unsigned, op %s */
+ warning(218, mp->m_name);
+ ln->tn_val->v_ansiu = 0;
+ }
+ if (mp->m_transiu && rn->tn_op == CON && rn->tn_val->v_ansiu) {
+ /* ANSI C treats constant as unsigned, op %s */
+ warning(218, mp->m_name);
+ rn->tn_val->v_ansiu = 0;
+ }
+
+ /* Make sure both operands are of the same type */
+ if (mp->m_balance || (tflag && (op == SHL || op == SHR)))
+ balance(op, &ln, &rn);
+
+ /*
+ * Check types for compatibility with the operation and mutual
+ * compatibility. Return if there are serios problems.
+ */
+ if (!typeok(op, 0, ln, rn))
+ return (NULL);
+
+ /* And now create the node. */
+ switch (op) {
+ case POINT:
+ case ARROW:
+ ntn = bldstr(op, ln, rn);
+ break;
+ case INCAFT:
+ case DECAFT:
+ case INCBEF:
+ case DECBEF:
+ ntn = bldincdec(op, ln);
+ break;
+ case AMPER:
+ ntn = bldamper(ln, 0);
+ break;
+ case STAR:
+ ntn = mktnode(STAR, ln->tn_type->t_subt, ln, NULL);
+ break;
+ case PLUS:
+ case MINUS:
+ ntn = bldplmi(op, ln, rn);
+ break;
+ case SHL:
+ case SHR:
+ ntn = bldshft(op, ln, rn);
+ break;
+ case COLON:
+ ntn = bldcol(ln, rn);
+ break;
+ case ASSIGN:
+ case MULASS:
+ case DIVASS:
+ case MODASS:
+ case ADDASS:
+ case SUBASS:
+ case SHLASS:
+ case SHRASS:
+ case ANDASS:
+ case XORASS:
+ case ORASS:
+ case RETURN:
+ ntn = bldasgn(op, ln, rn);
+ break;
+ case COMMA:
+ case QUEST:
+ ntn = mktnode(op, rn->tn_type, ln, rn);
+ break;
+ default:
+ rtp = mp->m_logop ? gettyp(INT) : ln->tn_type;
+ if (!mp->m_binary && rn != NULL)
+ lerror("build() 1");
+ ntn = mktnode(op, rtp, ln, rn);
+ break;
+ }
+
+ /* Return if an error occured. */
+ if (ntn == NULL)
+ return (NULL);
+
+ /* Print a warning if precedence confusion is possible */
+ if (mp->m_tpconf)
+ precconf(ntn);
+
+ /*
+ * Print a warning if one of the operands is in a context where
+ * it is compared with null and if this operand is a constant.
+ */
+ if (mp->m_tctx) {
+ if (ln->tn_op == CON ||
+ ((mp->m_binary && op != QUEST) && rn->tn_op == CON)) {
+ if (hflag && !ccflg)
+ /* constant in conditional context */
+ warning(161);
+ }
+ }
+
+ /* Fold if the operator requires it */
+ if (mp->m_fold) {
+ if (ln->tn_op == CON && (!mp->m_binary || rn->tn_op == CON)) {
+ if (mp->m_tctx) {
+ ntn = foldtst(ntn);
+ } else if (isftyp(ntn->tn_type->t_tspec)) {
+ ntn = foldflt(ntn);
+ } else {
+ ntn = fold(ntn);
+ }
+ } else if (op == QUEST && ln->tn_op == CON) {
+ ntn = ln->tn_val->v_quad ? rn->tn_left : rn->tn_right;
+ }
+ }
+
+ return (ntn);
+}
+
+/*
+ * Perform class conversions.
+ *
+ * Arrays of type T are converted into pointers to type T.
+ * Functions are converted to pointers to functions.
+ * Lvalues are converted to rvalues.
+ */
+tnode_t *
+cconv(tn)
+ tnode_t *tn;
+{
+ type_t *tp;
+
+ /*
+ * Array-lvalue (array of type T) is converted into rvalue
+ * (pointer to type T)
+ */
+ if (tn->tn_type->t_tspec == ARRAY) {
+ if (!tn->tn_lvalue) {
+ /* %soperand of '%s' must be lvalue */
+ /* XXX print correct operator */
+ (void)gnuism(114, "", modtab[AMPER].m_name);
+ }
+ tn = mktnode(AMPER, tincref(tn->tn_type->t_subt, PTR),
+ tn, NULL);
+ }
+
+ /*
+ * Expression of type function (function with return value of type T)
+ * in rvalue-expression (pointer to function with return value
+ * of type T)
+ */
+ if (tn->tn_type->t_tspec == FUNC)
+ tn = bldamper(tn, 1);
+
+ /* lvalue to rvalue */
+ if (tn->tn_lvalue) {
+ tp = tduptyp(tn->tn_type);
+ tp->t_const = tp->t_volatile = 0;
+ tn = mktnode(LOAD, tp, tn, NULL);
+ }
+
+ return (tn);
+}
+
+/*
+ * Perform most type checks. First the types are checked using
+ * informations from modtab[]. After that it is done by hand for
+ * more complicated operators and type combinations.
+ *
+ * If the types are ok, typeok() returns 1, otherwise 0.
+ */
+int
+typeok(op, arg, ln, rn)
+ op_t op;
+ int arg;
+ tnode_t *ln, *rn;
+{
+ mod_t *mp;
+ tspec_t lt, rt, lst, rst, olt, ort;
+ type_t *ltp, *rtp, *lstp, *rstp;
+ tnode_t *tn;
+
+ mp = &modtab[op];
+
+ if ((lt = (ltp = ln->tn_type)->t_tspec) == PTR)
+ lst = (lstp = ltp->t_subt)->t_tspec;
+ if (mp->m_binary) {
+ if ((rt = (rtp = rn->tn_type)->t_tspec) == PTR)
+ rst = (rstp = rtp->t_subt)->t_tspec;
+ }
+
+ if (mp->m_rqint) {
+ /* integertypes required */
+ if (!isityp(lt) || (mp->m_binary && !isityp(rt))) {
+ incompat(op, lt, rt);
+ return (0);
+ }
+ } else if (mp->m_rqsclt) {
+ /* scalar types required */
+ if (!issclt(lt) || (mp->m_binary && !issclt(rt))) {
+ incompat(op, lt, rt);
+ return (0);
+ }
+ } else if (mp->m_rqatyp) {
+ /* arithmetic types required */
+ if (!isatyp(lt) || (mp->m_binary && !isatyp(rt))) {
+ incompat(op, lt, rt);
+ return (0);
+ }
+ }
+
+ if (op == SHL || op == SHR || op == SHLASS || op == SHRASS) {
+ /*
+ * For these operations we need the types before promotion
+ * and balancing.
+ */
+ for (tn=ln; tn->tn_op==CVT && !tn->tn_cast; tn=tn->tn_left) ;
+ olt = tn->tn_type->t_tspec;
+ for (tn=rn; tn->tn_op==CVT && !tn->tn_cast; tn=tn->tn_left) ;
+ ort = tn->tn_type->t_tspec;
+ }
+
+ switch (op) {
+ case POINT:
+ /*
+ * Most errors required by ANSI C are reported in strmemb().
+ * Here we only must check for totaly wrong things.
+ */
+ if (lt == FUNC || lt == VOID || ltp->t_isfield ||
+ ((lt != STRUCT && lt != UNION) && !ln->tn_lvalue)) {
+ /* Without tflag we got already an error */
+ if (tflag)
+ /* unacceptable operand of %s */
+ error(111, mp->m_name);
+ return (0);
+ }
+ /* Now we have an object we can create a pointer to */
+ break;
+ case ARROW:
+ if (lt != PTR && !(tflag && isityp(lt))) {
+ /* Without tflag we got already an error */
+ if (tflag)
+ /* unacceptabel operand of %s */
+ error(111, mp->m_name);
+ return (0);
+ }
+ break;
+ case INCAFT:
+ case DECAFT:
+ case INCBEF:
+ case DECBEF:
+ /* operands have scalar types (checked above) */
+ if (!ln->tn_lvalue) {
+ if (ln->tn_op == CVT && ln->tn_cast &&
+ ln->tn_left->tn_op == LOAD) {
+ /* a cast does not yield an lvalue */
+ error(163);
+ }
+ /* %soperand of %s must be lvalue */
+ error(114, "", mp->m_name);
+ return (0);
+ } else if (ltp->t_const) {
+ /* %soperand of %s must be modifiable lvalue */
+ if (!tflag)
+ warning(115, "", mp->m_name);
+ }
+ break;
+ case AMPER:
+ if (lt == ARRAY || lt == FUNC) {
+ /* ok, a warning comes later (in bldamper()) */
+ } else if (!ln->tn_lvalue) {
+ if (ln->tn_op == CVT && ln->tn_cast &&
+ ln->tn_left->tn_op == LOAD) {
+ /* a cast does not yield an lvalue */
+ error(163);
+ }
+ /* %soperand of %s must be lvalue */
+ error(114, "", mp->m_name);
+ return (0);
+ } else if (issclt(lt)) {
+ if (ltp->t_isfield) {
+ /* cannot take address of bit-field */
+ error(112);
+ return (0);
+ }
+ } else if (lt != STRUCT && lt != UNION) {
+ /* unacceptable operand of %s */
+ error(111, mp->m_name);
+ return (0);
+ }
+ if (ln->tn_op == NAME && ln->tn_sym->s_reg) {
+ /* cannot take address of register %s */
+ error(113, ln->tn_sym->s_name);
+ return (0);
+ }
+ break;
+ case STAR:
+ /* until now there were no type checks for this operator */
+ if (lt != PTR) {
+ /* cannot dereference non-pointer type */
+ error(96);
+ return (0);
+ }
+ break;
+ case PLUS:
+ /* operands have scalar types (checked above) */
+ if ((lt == PTR && !isityp(rt)) || (rt == PTR && !isityp(lt))) {
+ incompat(op, lt, rt);
+ return (0);
+ }
+ break;
+ case MINUS:
+ /* operands have scalar types (checked above) */
+ if (lt == PTR && (!isityp(rt) && rt != PTR)) {
+ incompat(op, lt, rt);
+ return (0);
+ } else if (rt == PTR && lt != PTR) {
+ incompat(op, lt, rt);
+ return (0);
+ }
+ if (lt == PTR && rt == PTR) {
+ if (!eqtype(lstp, rstp, 1, 0, NULL)) {
+ /* illegal pointer subtraction */
+ error(116);
+ }
+ }
+ break;
+ case SHR:
+ /* operands have integer types (checked above) */
+ if (pflag && !isutyp(lt)) {
+ /*
+ * The left operand is signed. This means that
+ * the operation is (possibly) nonportable.
+ */
+ /* bitwise operation on signed value nonportable */
+ if (ln->tn_op != CON) {
+ /* possibly nonportable */
+ warning(117);
+ } else if (ln->tn_val->v_quad < 0) {
+ warning(120);
+ }
+ } else if (!tflag && !sflag && !isutyp(olt) && isutyp(ort)) {
+ /*
+ * The left operand would become unsigned in
+ * traditional C.
+ */
+ if (hflag &&
+ (ln->tn_op != CON || ln->tn_val->v_quad < 0)) {
+ /* semantics of %s change in ANSI C; use ... */
+ warning(118, mp->m_name);
+ }
+ } else if (!tflag && !sflag && !isutyp(olt) && !isutyp(ort) &&
+ psize(lt) < psize(rt)) {
+ /*
+ * In traditional C the left operand would be extended,
+ * possibly with 1, and then shifted.
+ */
+ if (hflag &&
+ (ln->tn_op != CON || ln->tn_val->v_quad < 0)) {
+ /* semantics of %s change in ANSI C; use ... */
+ warning(118, mp->m_name);
+ }
+ }
+ goto shift;
+ case SHL:
+ /*
+ * ANSI C does not perform balancing for shift operations,
+ * but traditional C does. If the width of the right operand
+ * is greather than the width of the left operand, than in
+ * traditional C the left operand would be extendet to the
+ * width of the right operand. For SHL this may result in
+ * different results.
+ */
+ if (psize(lt) < psize(rt)) {
+ /*
+ * XXX If both operands are constant make sure
+ * that there is really a differencs between
+ * ANSI C and traditional C.
+ */
+ if (hflag)
+ /* semantics of %s change in ANSI C; use ... */
+ warning(118, mp->m_name);
+ }
+ shift:
+ if (rn->tn_op == CON) {
+ if (!isutyp(rt) && rn->tn_val->v_quad < 0) {
+ /* negative shift */
+ warning(121);
+ } else if ((u_quad_t)rn->tn_val->v_quad == size(lt)) {
+ /* shift equal to size fo object */
+ warning(267);
+ } else if ((u_quad_t)rn->tn_val->v_quad > size(lt)) {
+ /* shift greater than size of object */
+ warning(122);
+ }
+ }
+ break;
+ case EQ:
+ case NE:
+ /*
+ * Accept some things which are allowed with EQ and NE,
+ * but not with ordered comparisions.
+ */
+ if (lt == PTR && ((rt == PTR && rst == VOID) || isityp(rt))) {
+ if (rn->tn_op == CON && rn->tn_val->v_quad == 0)
+ break;
+ }
+ if (rt == PTR && ((lt == PTR && lst == VOID) || isityp(lt))) {
+ if (ln->tn_op == CON && ln->tn_val->v_quad == 0)
+ break;
+ }
+ /* FALLTHROUGH */
+ case LT:
+ case GT:
+ case LE:
+ case GE:
+ if ((lt == PTR || rt == PTR) && lt != rt) {
+ if (isityp(lt) || isityp(rt)) {
+ /* illegal comb. of pointer and int., op %s */
+ warning(123, mp->m_name);
+ } else {
+ incompat(op, lt, rt);
+ return (0);
+ }
+ } else if (lt == PTR && rt == PTR) {
+ ptrcmpok(op, ln, rn);
+ }
+ break;
+ case QUEST:
+ if (!issclt(lt)) {
+ /* first operand must have scalar type, op ? : */
+ error(170);
+ return (0);
+ }
+ if (rn->tn_op != COLON)
+ lerror("typeok() 2");
+ break;
+ case COLON:
+
+ if (isatyp(lt) && isatyp(rt))
+ break;
+
+ if (lt == STRUCT && rt == STRUCT && ltp->t_str == rtp->t_str)
+ break;
+ if (lt == UNION && rt == UNION && ltp->t_str == rtp->t_str)
+ break;
+
+ /* combination of any pointer and 0, 0L or (void *)0 is ok */
+ if (lt == PTR && ((rt == PTR && rst == VOID) || isityp(rt))) {
+ if (rn->tn_op == CON && rn->tn_val->v_quad == 0)
+ break;
+ }
+ if (rt == PTR && ((lt == PTR && lst == VOID) || isityp(lt))) {
+ if (ln->tn_op == CON && ln->tn_val->v_quad == 0)
+ break;
+ }
+
+ if ((lt == PTR && isityp(rt)) || (isityp(lt) && rt == PTR)) {
+ /* illegal comb. of ptr. and int., op %s */
+ warning(123, mp->m_name);
+ break;
+ }
+
+ if (lt == VOID || rt == VOID) {
+ if (lt != VOID || rt != VOID)
+ /* incompatible types in conditional */
+ warning(126);
+ break;
+ }
+
+ if (lt == PTR && rt == PTR && ((lst == VOID && rst == FUNC) ||
+ (lst == FUNC && rst == VOID))) {
+ /* (void *)0 handled above */
+ if (sflag)
+ /* ANSI C forbids conv. of %s to %s, op %s */
+ warning(305, "function pointer", "'void *'",
+ mp->m_name);
+ break;
+ }
+
+ if (rt == PTR && lt == PTR) {
+ if (!eqtype(lstp, rstp, 1, 0, NULL))
+ illptrc(mp, ltp, rtp);
+ break;
+ }
+
+ /* incompatible types in conditional */
+ error(126);
+ return (0);
+
+ case ASSIGN:
+ case INIT:
+ case FARG:
+ case RETURN:
+ if (!asgntypok(op, arg, ln, rn))
+ return (0);
+ goto assign;
+ case MULASS:
+ case DIVASS:
+ case MODASS:
+ goto assign;
+ case ADDASS:
+ case SUBASS:
+ /* operands have scalar types (checked above) */
+ if ((lt == PTR && !isityp(rt)) || rt == PTR) {
+ incompat(op, lt, rt);
+ return (0);
+ }
+ goto assign;
+ case SHLASS:
+ goto assign;
+ case SHRASS:
+ if (pflag && !isutyp(lt) && !(tflag && isutyp(rt))) {
+ /* bitwise operation on s.v. possibly nonportabel */
+ warning(117);
+ }
+ goto assign;
+ case ANDASS:
+ case XORASS:
+ case ORASS:
+ goto assign;
+ assign:
+ if (!ln->tn_lvalue) {
+ if (ln->tn_op == CVT && ln->tn_cast &&
+ ln->tn_left->tn_op == LOAD) {
+ /* a cast does not yield an lvalue */
+ error(163);
+ }
+ /* %soperand of %s must be lvalue */
+ error(114, "left ", mp->m_name);
+ return (0);
+ } else if (ltp->t_const || ((lt == STRUCT || lt == UNION) &&
+ conmemb(ltp))) {
+ /* %soperand of %s must be modifiable lvalue */
+ if (!tflag)
+ warning(115, "left ", mp->m_name);
+ }
+ break;
+ case COMMA:
+ if (!modtab[ln->tn_op].m_sideeff)
+ nulleff(ln);
+ break;
+ /* LINTED (enumeration values not handled in switch) */
+ default:
+ }
+
+ if (mp->m_badeop &&
+ (ltp->t_isenum || (mp->m_binary && rtp->t_isenum))) {
+ chkbeop(op, ln, rn);
+ } else if (mp->m_enumop && (ltp->t_isenum && rtp->t_isenum)) {
+ chkeop2(op, arg, ln, rn);
+ } else if (mp->m_enumop && (ltp->t_isenum || rtp->t_isenum)) {
+ chkeop1(op, arg, ln, rn);
+ }
+
+ return (1);
+}
+
+static void
+ptrcmpok(op, ln, rn)
+ op_t op;
+ tnode_t *ln, *rn;
+{
+ type_t *ltp, *rtp;
+ tspec_t lt, rt;
+ const char *lts, *rts;
+
+ lt = (ltp = ln->tn_type)->t_subt->t_tspec;
+ rt = (rtp = rn->tn_type)->t_subt->t_tspec;
+
+ if (lt == VOID || rt == VOID) {
+ if (sflag && (lt == FUNC || rt == FUNC)) {
+ /* (void *)0 already handled in typeok() */
+ *(lt == FUNC ? &lts : &rts) = "function pointer";
+ *(lt == VOID ? &lts : &rts) = "'void *'";
+ /* ANSI C forbids comparision of %s with %s */
+ warning(274, lts, rts);
+ }
+ return;
+ }
+
+ if (!eqtype(ltp->t_subt, rtp->t_subt, 1, 0, NULL)) {
+ illptrc(&modtab[op], ltp, rtp);
+ return;
+ }
+
+ if (lt == FUNC && rt == FUNC) {
+ if (sflag && op != EQ && op != NE)
+ /* ANSI C forbids ordered comp. of func ptr */
+ warning(125);
+ }
+}
+
+/*
+ * Checks type compatibility for ASSIGN, INIT, FARG and RETURN
+ * and prints warnings/errors if necessary.
+ * If the types are (almost) compatible, 1 is returned, otherwise 0.
+ */
+static int
+asgntypok(op, arg, ln, rn)
+ op_t op;
+ int arg;
+ tnode_t *ln, *rn;
+{
+ tspec_t lt, rt, lst, rst;
+ type_t *ltp, *rtp, *lstp, *rstp;
+ mod_t *mp;
+ const char *lts, *rts;
+
+ if ((lt = (ltp = ln->tn_type)->t_tspec) == PTR)
+ lst = (lstp = ltp->t_subt)->t_tspec;
+ if ((rt = (rtp = rn->tn_type)->t_tspec) == PTR)
+ rst = (rstp = rtp->t_subt)->t_tspec;
+ mp = &modtab[op];
+
+ if (isatyp(lt) && isatyp(rt))
+ return (1);
+
+ if ((lt == STRUCT || lt == UNION) && (rt == STRUCT || rt == UNION))
+ /* both are struct or union */
+ return (ltp->t_str == rtp->t_str);
+
+ /* 0, 0L and (void *)0 may be assigned to any pointer */
+ if (lt == PTR && ((rt == PTR && rst == VOID) || isityp(rt))) {
+ if (rn->tn_op == CON && rn->tn_val->v_quad == 0)
+ return (1);
+ }
+
+ if (lt == PTR && rt == PTR && (lst == VOID || rst == VOID)) {
+ /* two pointers, at least one pointer to void */
+ if (sflag && (lst == FUNC || rst == FUNC)) {
+ /* comb. of ptr to func and ptr to void */
+ *(lst == FUNC ? &lts : &rts) = "function pointer";
+ *(lst == VOID ? &lts : &rts) = "'void *'";
+ switch (op) {
+ case INIT:
+ case RETURN:
+ /* ANSI C forbids conversion of %s to %s */
+ warning(303, rts, lts);
+ break;
+ case FARG:
+ /* ANSI C forbids conv. of %s to %s, arg #%d */
+ warning(304, rts, lts, arg);
+ break;
+ default:
+ /* ANSI C forbids conv. of %s to %s, op %s */
+ warning(305, rts, lts, mp->m_name);
+ break;
+ }
+ }
+ }
+
+ if (lt == PTR && rt == PTR && (lst == VOID || rst == VOID ||
+ eqtype(lstp, rstp, 1, 0, NULL))) {
+ /* compatible pointer types (qualifiers ignored) */
+ if (!tflag &&
+ ((!lstp->t_const && rstp->t_const) ||
+ (!lstp->t_volatile && rstp->t_volatile))) {
+ /* left side has not all qualifiers of right */
+ switch (op) {
+ case INIT:
+ case RETURN:
+ /* incompatible pointer types */
+ warning(182);
+ break;
+ case FARG:
+ /* argument has incompat. ptr. type, arg #%d */
+ warning(153, arg);
+ break;
+ default:
+ /* operands have incompat. ptr. types, op %s */
+ warning(128, mp->m_name);
+ break;
+ }
+ }
+ return (1);
+ }
+
+ if ((lt == PTR && isityp(rt)) || (isityp(lt) && rt == PTR)) {
+ switch (op) {
+ case INIT:
+ case RETURN:
+ /* illegal combination of pointer and integer */
+ warning(183);
+ break;
+ case FARG:
+ /* illegal comb. of ptr. and int., arg #%d */
+ warning(154, arg);
+ break;
+ default:
+ /* illegal comb. of ptr. and int., op %s */
+ warning(123, mp->m_name);
+ break;
+ }
+ return (1);
+ }
+
+ if (lt == PTR && rt == PTR) {
+ switch (op) {
+ case INIT:
+ case RETURN:
+ illptrc(NULL, ltp, rtp);
+ break;
+ case FARG:
+ /* argument has incompatible pointer type, arg #%d */
+ warning(153, arg);
+ break;
+ default:
+ illptrc(mp, ltp, rtp);
+ break;
+ }
+ return (1);
+ }
+
+ switch (op) {
+ case INIT:
+ /* initialisation type mismatch */
+ error(185);
+ break;
+ case RETURN:
+ /* return value type mismatch */
+ error(211);
+ break;
+ case FARG:
+ /* argument is incompatible with prototype, arg #%d */
+ warning(155, arg);
+ break;
+ default:
+ incompat(op, lt, rt);
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Prints a warning if an operator, which should be senseless for an
+ * enum type, is applied to an enum type.
+ */
+static void
+chkbeop(op, ln, rn)
+ op_t op;
+ tnode_t *ln, *rn;
+{
+ mod_t *mp;
+
+ if (!eflag)
+ return;
+
+ mp = &modtab[op];
+
+ if (!(ln->tn_type->t_isenum ||
+ (mp->m_binary && rn->tn_type->t_isenum))) {
+ return;
+ }
+
+ /*
+ * Enum as offset to a pointer is an exception (otherwise enums
+ * could not be used as array indizes).
+ */
+ if (op == PLUS &&
+ ((ln->tn_type->t_isenum && rn->tn_type->t_tspec == PTR) ||
+ (rn->tn_type->t_isenum && ln->tn_type->t_tspec == PTR))) {
+ return;
+ }
+
+ /* dubious operation on enum, op %s */
+ warning(241, mp->m_name);
+
+}
+
+/*
+ * Prints a warning if an operator is applied to two different enum types.
+ */
+static void
+chkeop2(op, arg, ln, rn)
+ op_t op;
+ int arg;
+ tnode_t *ln, *rn;
+{
+ mod_t *mp;
+
+ mp = &modtab[op];
+
+ if (ln->tn_type->t_enum != rn->tn_type->t_enum) {
+ switch (op) {
+ case INIT:
+ /* enum type mismatch in initialisation */
+ warning(210);
+ break;
+ case FARG:
+ /* enum type mismatch, arg #%d */
+ warning(156, arg);
+ break;
+ case RETURN:
+ /* return value type mismatch */
+ warning(211);
+ break;
+ default:
+ /* enum type mismatch, op %s */
+ warning(130, mp->m_name);
+ break;
+ }
+#if 0
+ } else if (mp->m_comp && op != EQ && op != NE) {
+ if (eflag)
+ /* dubious comparisions of enums */
+ warning(243, mp->m_name);
+#endif
+ }
+}
+
+/*
+ * Prints a warning if an operator has both enum end other integer
+ * types.
+ */
+static void
+chkeop1(op, arg, ln, rn)
+ op_t op;
+ int arg;
+ tnode_t *ln, *rn;
+{
+ if (!eflag)
+ return;
+
+ switch (op) {
+ case INIT:
+ /*
+ * Initializations with 0 should be allowed. Otherwise,
+ * we should complain about all uninitialized enums,
+ * consequently.
+ */
+ if (!rn->tn_type->t_isenum && rn->tn_op == CON &&
+ isityp(rn->tn_type->t_tspec) && rn->tn_val->v_quad == 0) {
+ return;
+ }
+ /* initialisation of '%s' with '%s' */
+ warning(277, tyname(ln->tn_type), tyname(rn->tn_type));
+ break;
+ case FARG:
+ /* combination of '%s' and '%s', arg #%d */
+ warning(278, tyname(ln->tn_type), tyname(rn->tn_type), arg);
+ break;
+ case RETURN:
+ /* combination of '%s' and '%s' in return */
+ warning(279, tyname(ln->tn_type), tyname(rn->tn_type));
+ break;
+ default:
+ /* combination of '%s' and %s, op %s */
+ warning(242, tyname(ln->tn_type), tyname(rn->tn_type),
+ modtab[op].m_name);
+ break;
+ }
+}
+
+/*
+ * Build and initialize a new node.
+ */
+static tnode_t *
+mktnode(op, type, ln, rn)
+ op_t op;
+ type_t *type;
+ tnode_t *ln, *rn;
+{
+ tnode_t *ntn;
+ tspec_t t;
+
+ ntn = getnode();
+
+ ntn->tn_op = op;
+ ntn->tn_type = type;
+ ntn->tn_left = ln;
+ ntn->tn_right = rn;
+
+ if (op == STAR || op == FSEL) {
+ if (ln->tn_type->t_tspec == PTR) {
+ t = ln->tn_type->t_subt->t_tspec;
+ if (t != FUNC && t != VOID)
+ ntn->tn_lvalue = 1;
+ } else {
+ lerror("mktnode() 2");
+ }
+ }
+
+ return (ntn);
+}
+
+/*
+ * Performs usual conversion of operands to (unsigned) int.
+ *
+ * If tflag is set or the operand is a function argument with no
+ * type information (no prototype or variable # of args), convert
+ * float to double.
+ */
+tnode_t *
+promote(op, farg, tn)
+ op_t op;
+ int farg;
+ tnode_t *tn;
+{
+ tspec_t t;
+ type_t *ntp;
+ int len;
+
+ t = tn->tn_type->t_tspec;
+
+ if (!isatyp(t))
+ return (tn);
+
+ if (!tflag) {
+ /*
+ * ANSI C requires that the result is always of type INT
+ * if INT can represent all possible values of the previous
+ * type.
+ */
+ if (tn->tn_type->t_isfield) {
+ len = tn->tn_type->t_flen;
+ if (size(INT) > len) {
+ t = INT;
+ } else {
+ if (size(INT) != len)
+ lerror("promote() 1");
+ if (isutyp(t)) {
+ t = UINT;
+ } else {
+ t = INT;
+ }
+ }
+ } else if (t == CHAR || t == UCHAR || t == SCHAR) {
+ t = (size(CHAR) < size(INT) || t != UCHAR) ?
+ INT : UINT;
+ } else if (t == SHORT || t == USHORT) {
+ t = (size(SHORT) < size(INT) || t == SHORT) ?
+ INT : UINT;
+ } else if (t == ENUM) {
+ t = INT;
+ } else if (farg && t == FLOAT) {
+ t = DOUBLE;
+ }
+ } else {
+ /*
+ * In traditional C, keep unsigned and promote FLOAT
+ * to DOUBLE.
+ */
+ if (t == UCHAR || t == USHORT) {
+ t = UINT;
+ } else if (t == CHAR || t == SCHAR || t == SHORT) {
+ t = INT;
+ } else if (t == FLOAT) {
+ t = DOUBLE;
+ } else if (t == ENUM) {
+ t = INT;
+ }
+ }
+
+ if (t != tn->tn_type->t_tspec) {
+ ntp = tduptyp(tn->tn_type);
+ ntp->t_tspec = t;
+ /*
+ * Keep t_isenum so we are later able to check compatibility
+ * of enum types.
+ */
+ tn = convert(op, 0, ntp, tn);
+ }
+
+ return (tn);
+}
+
+/*
+ * Insert conversions which are necessary to give both operands the same
+ * type. This is done in different ways for traditional C and ANIS C.
+ */
+static void
+balance(op, lnp, rnp)
+ op_t op;
+ tnode_t **lnp, **rnp;
+{
+ tspec_t lt, rt, t;
+ int i, u;
+ type_t *ntp;
+ static tspec_t tl[] = {
+ LDOUBLE, DOUBLE, FLOAT, UQUAD, QUAD, ULONG, LONG, UINT, INT,
+ };
+
+ lt = (*lnp)->tn_type->t_tspec;
+ rt = (*rnp)->tn_type->t_tspec;
+
+ if (!isatyp(lt) || !isatyp(rt))
+ return;
+
+ if (!tflag) {
+ if (lt == rt) {
+ t = lt;
+ } else if (lt == LDOUBLE || rt == LDOUBLE) {
+ t = LDOUBLE;
+ } else if (lt == DOUBLE || rt == DOUBLE) {
+ t = DOUBLE;
+ } else if (lt == FLOAT || rt == FLOAT) {
+ t = FLOAT;
+ } else {
+ /*
+ * If type A has more bits than type B it should
+ * be able to hold all possible values of type B.
+ */
+ if (size(lt) > size(rt)) {
+ t = lt;
+ } else if (size(lt) < size(rt)) {
+ t = rt;
+ } else {
+ for (i = 3; tl[i] != INT; i++) {
+ if (tl[i] == lt || tl[i] == rt)
+ break;
+ }
+ if ((isutyp(lt) || isutyp(rt)) &&
+ !isutyp(tl[i])) {
+ i--;
+ }
+ t = tl[i];
+ }
+ }
+ } else {
+ /* Keep unsigned in traditional C */
+ u = isutyp(lt) || isutyp(rt);
+ for (i = 0; tl[i] != INT; i++) {
+ if (lt == tl[i] || rt == tl[i])
+ break;
+ }
+ t = tl[i];
+ if (u && isityp(t) && !isutyp(t))
+ t = utyp(t);
+ }
+
+ if (t != lt) {
+ ntp = tduptyp((*lnp)->tn_type);
+ ntp->t_tspec = t;
+ *lnp = convert(op, 0, ntp, *lnp);
+ }
+ if (t != rt) {
+ ntp = tduptyp((*rnp)->tn_type);
+ ntp->t_tspec = t;
+ *rnp = convert(op, 0, ntp, *rnp);
+ }
+}
+
+/*
+ * Insert a conversion operator, which converts the type of the node
+ * to another given type.
+ * If op is FARG, arg is the number of the argument (used for warnings).
+ */
+tnode_t *
+convert(op, arg, tp, tn)
+ op_t op;
+ int arg;
+ type_t *tp;
+ tnode_t *tn;
+{
+ tnode_t *ntn;
+ tspec_t nt, ot, ost;
+
+ if (tn->tn_lvalue)
+ lerror("convert() 1");
+
+ nt = tp->t_tspec;
+ if ((ot = tn->tn_type->t_tspec) == PTR)
+ ost = tn->tn_type->t_subt->t_tspec;
+
+ if (!tflag && !sflag && op == FARG)
+ ptconv(arg, nt, ot, tp, tn);
+ if (isityp(nt) && isityp(ot)) {
+ iiconv(op, arg, nt, ot, tp, tn);
+ } else if (nt == PTR && ((ot == PTR && ost == VOID) || isityp(ot)) &&
+ tn->tn_op == CON && tn->tn_val->v_quad == 0) {
+ /* 0, 0L and (void *)0 may be assigned to any pointer. */
+ } else if (isityp(nt) && ot == PTR) {
+ piconv(op, nt, tp, tn);
+ } else if (nt == PTR && ot == PTR) {
+ ppconv(op, tn, tp);
+ }
+
+ ntn = getnode();
+ ntn->tn_op = CVT;
+ ntn->tn_type = tp;
+ ntn->tn_cast = op == CVT;
+ if (tn->tn_op != CON || nt == VOID) {
+ ntn->tn_left = tn;
+ } else {
+ ntn->tn_op = CON;
+ ntn->tn_val = tgetblk(sizeof (val_t));
+ cvtcon(op, arg, ntn->tn_type, ntn->tn_val, tn->tn_val);
+ }
+
+ return (ntn);
+}
+
+/*
+ * Print a warning if a prototype causes a type conversion that is
+ * different from what would happen to the same argument in the
+ * absence of a prototype.
+ *
+ * Errors/Warnings about illegal type combinations are already printed
+ * in asgntypok().
+ */
+static void
+ptconv(arg, nt, ot, tp, tn)
+ int arg;
+ tspec_t nt, ot;
+ type_t *tp;
+ tnode_t *tn;
+{
+ tnode_t *ptn;
+
+ if (!isatyp(nt) || !isatyp(ot))
+ return;
+
+ /*
+ * If the type of the formal parameter is char/short, a warning
+ * would be useless, because functions declared the old style
+ * can't expect char/short arguments.
+ */
+ if (nt == CHAR || nt == UCHAR || nt == SHORT || nt == USHORT)
+ return;
+
+ /* get default promotion */
+ ptn = promote(NOOP, 1, tn);
+ ot = ptn->tn_type->t_tspec;
+
+ /* return if types are the same with and without prototype */
+ if (nt == ot || (nt == ENUM && ot == INT))
+ return;
+
+ if (isftyp(nt) != isftyp(ot) || psize(nt) != psize(ot)) {
+ /* representation and/or width change */
+ if (styp(nt) != SHORT || !isityp(ot) || psize(ot) > psize(INT))
+ /* conversion to '%s' due to prototype, arg #%d */
+ warning(259, tyname(tp), arg);
+ } else if (hflag) {
+ /*
+ * they differ in sign or base type (char, short, int,
+ * long, long long, float, double, long double)
+ *
+ * if they differ only in sign and the argument is a constant
+ * and the msb of the argument is not set, print no warning
+ */
+ if (ptn->tn_op == CON && isityp(nt) && styp(nt) == styp(ot) &&
+ msb(ptn->tn_val->v_quad, ot, -1) == 0) {
+ /* ok */
+ } else {
+ /* conversion to '%s' due to prototype, arg #%d */
+ warning(259, tyname(tp), arg);
+ }
+ }
+}
+
+/*
+ * Print warnings for conversions of integer types which my cause
+ * problems.
+ */
+/* ARGSUSED */
+static void
+iiconv(op, arg, nt, ot, tp, tn)
+ op_t op;
+ int arg;
+ tspec_t nt, ot;
+ type_t *tp;
+ tnode_t *tn;
+{
+ if (tn->tn_op == CON)
+ return;
+
+ if (op == CVT)
+ return;
+
+#if 0
+ if (psize(nt) > psize(ot) && isutyp(nt) != isutyp(ot)) {
+ /* conversion to %s may sign-extend incorrectly (, arg #%d) */
+ if (aflag && pflag) {
+ if (op == FARG) {
+ warning(297, tyname(tp), arg);
+ } else {
+ warning(131, tyname(tp));
+ }
+ }
+ }
+#endif
+
+ if (psize(nt) < psize(ot) &&
+ (ot == LONG || ot == ULONG || ot == QUAD || ot == UQUAD ||
+ aflag > 1)) {
+ /* conversion from '%s' may lose accuracy */
+ if (aflag) {
+ if (op == FARG) {
+ warning(298, tyname(tn->tn_type), arg);
+ } else {
+ warning(132, tyname(tn->tn_type));
+ }
+ }
+ }
+}
+
+/*
+ * Print warnings for dubious conversions of pointer to integer.
+ */
+static void
+piconv(op, nt, tp, tn)
+ op_t op;
+ tspec_t nt;
+ type_t *tp;
+ tnode_t *tn;
+{
+ if (tn->tn_op == CON)
+ return;
+
+ if (op != CVT) {
+ /* We got already an error. */
+ return;
+ }
+
+ if (psize(nt) < psize(PTR)) {
+ if (pflag && size(nt) >= size(PTR)) {
+ /* conv. of pointer to %s may lose bits */
+ warning(134, tyname(tp));
+ } else {
+ /* conv. of pointer to %s loses bits */
+ warning(133, tyname(tp));
+ }
+ }
+}
+
+/*
+ * Print warnings for questionable pointer conversions.
+ */
+static void
+ppconv(op, tn, tp)
+ op_t op;
+ tnode_t *tn;
+ type_t *tp;
+{
+ tspec_t nt, ot;
+ const char *nts, *ots;
+
+ /*
+ * We got already an error (pointers of different types
+ * without a cast) or we will not get a warning.
+ */
+ if (op != CVT)
+ return;
+
+ nt = tp->t_subt->t_tspec;
+ ot = tn->tn_type->t_subt->t_tspec;
+
+ if (nt == VOID || ot == VOID) {
+ if (sflag && (nt == FUNC || ot == FUNC)) {
+ /* (void *)0 already handled in convert() */
+ *(nt == FUNC ? &nts : &ots) = "function pointer";
+ *(nt == VOID ? &nts : &ots) = "'void *'";
+ /* ANSI C forbids conversion of %s to %s */
+ warning(303, ots, nts);
+ }
+ return;
+ } else if (nt == FUNC && ot == FUNC) {
+ return;
+ } else if (nt == FUNC || ot == FUNC) {
+ /* questionable conversion of function pointer */
+ warning(229);
+ return;
+ }
+
+ if (getbound(tp->t_subt) > getbound(tn->tn_type->t_subt)) {
+ if (hflag)
+ /* possible pointer alignment problem */
+ warning(135);
+ }
+ if (((nt == STRUCT || nt == UNION) &&
+ tp->t_subt->t_str != tn->tn_type->t_subt->t_str) ||
+ psize(nt) != psize(ot)) {
+ if (cflag) {
+ /* pointer casts may be troublesome */
+ warning(247);
+ }
+ }
+}
+
+/*
+ * Converts a typed constant in a constant of another type.
+ *
+ * op operator which requires conversion
+ * arg if op is FARG, # of argument
+ * tp type in which to convert the constant
+ * nv new constant
+ * v old constant
+ */
+void
+cvtcon(op, arg, tp, nv, v)
+ op_t op;
+ int arg;
+ type_t *tp;
+ val_t *nv, *v;
+{
+ tspec_t ot, nt;
+ ldbl_t max, min;
+ int sz, rchk;
+ quad_t xmask, xmsk1;
+ int osz, nsz;
+
+ ot = v->v_tspec;
+ nt = nv->v_tspec = tp->t_tspec;
+ rchk = 0;
+
+ if (ot == FLOAT || ot == DOUBLE || ot == LDOUBLE) {
+ switch (nt) {
+ case CHAR:
+ max = CHAR_MAX; min = CHAR_MIN; break;
+ case UCHAR:
+ max = UCHAR_MAX; min = 0; break;
+ case SCHAR:
+ max = SCHAR_MAX; min = SCHAR_MIN; break;
+ case SHORT:
+ max = SHRT_MAX; min = SHRT_MIN; break;
+ case USHORT:
+ max = USHRT_MAX; min = 0; break;
+ case ENUM:
+ case INT:
+ max = INT_MAX; min = INT_MIN; break;
+ case UINT:
+ max = (u_int)UINT_MAX; min = 0; break;
+ case LONG:
+ max = LONG_MAX; min = LONG_MIN; break;
+ case ULONG:
+ max = (u_long)ULONG_MAX; min = 0; break;
+ case QUAD:
+ max = QUAD_MAX; min = QUAD_MIN; break;
+ case UQUAD:
+ max = (u_quad_t)UQUAD_MAX; min = 0; break;
+ case FLOAT:
+ max = FLT_MAX; min = -FLT_MAX; break;
+ case DOUBLE:
+ max = DBL_MAX; min = -DBL_MAX; break;
+ case PTR:
+ /* Got already an error because of float --> ptr */
+ case LDOUBLE:
+ max = LDBL_MAX; min = -LDBL_MAX; break;
+ default:
+ lerror("cvtcon() 1");
+ }
+ if (v->v_ldbl > max || v->v_ldbl < min) {
+ if (nt == LDOUBLE)
+ lerror("cvtcon() 2");
+ if (op == FARG) {
+ /* conv. of %s to %s is out of rng., arg #%d */
+ warning(295, tyname(gettyp(ot)), tyname(tp),
+ arg);
+ } else {
+ /* conversion of %s to %s is out of range */
+ warning(119, tyname(gettyp(ot)), tyname(tp));
+ }
+ v->v_ldbl = v->v_ldbl > 0 ? max : min;
+ }
+ if (nt == FLOAT) {
+ nv->v_ldbl = (float)v->v_ldbl;
+ } else if (nt == DOUBLE) {
+ nv->v_ldbl = (double)v->v_ldbl;
+ } else if (nt == LDOUBLE) {
+ nv->v_ldbl = v->v_ldbl;
+ } else {
+ nv->v_quad = (nt == PTR || isutyp(nt)) ?
+ (u_quad_t)v->v_ldbl : (quad_t)v->v_ldbl;
+ }
+ } else {
+ if (nt == FLOAT) {
+ nv->v_ldbl = (ot == PTR || isutyp(ot)) ?
+ (float)(u_quad_t)v->v_quad : (float)v->v_quad;
+ } else if (nt == DOUBLE) {
+ nv->v_ldbl = (ot == PTR || isutyp(ot)) ?
+ (double)(u_quad_t)v->v_quad : (double)v->v_quad;
+ } else if (nt == LDOUBLE) {
+ nv->v_ldbl = (ot == PTR || isutyp(ot)) ?
+ (ldbl_t)(u_quad_t)v->v_quad : (ldbl_t)v->v_quad;
+ } else {
+ rchk = 1; /* Check for lost precision. */
+ nv->v_quad = v->v_quad;
+ }
+ }
+
+ if (v->v_ansiu && isftyp(nt)) {
+ /* ANSI C treats constant as unsigned */
+ warning(157);
+ v->v_ansiu = 0;
+ } else if (v->v_ansiu && (isityp(nt) && !isutyp(nt) &&
+ psize(nt) > psize(ot))) {
+ /* ANSI C treats constant as unsigned */
+ warning(157);
+ v->v_ansiu = 0;
+ }
+
+ if (nt != FLOAT && nt != DOUBLE && nt != LDOUBLE) {
+ sz = tp->t_isfield ? tp->t_flen : size(nt);
+ nv->v_quad = xsign(nv->v_quad, nt, sz);
+ }
+
+ if (rchk && op != CVT) {
+ osz = size(ot);
+ nsz = tp->t_isfield ? tp->t_flen : size(nt);
+ xmask = qlmasks[nsz] ^ qlmasks[osz];
+ xmsk1 = qlmasks[nsz] ^ qlmasks[osz - 1];
+ /*
+ * For bitwise operations we are not interested in the
+ * value, but in the bits itself.
+ */
+ if (op == ORASS || op == OR || op == XOR) {
+ /*
+ * Print a warning if bits which were set are
+ * lost due to the conversion.
+ * This can happen with operator ORASS only.
+ */
+ if (nsz < osz && (v->v_quad & xmask) != 0) {
+ /* constant truncated by conv., op %s */
+ warning(306, modtab[op].m_name);
+ }
+ } else if (op == ANDASS || op == AND) {
+ /*
+ * Print a warning if additional bits are not all 1
+ * and the most significant bit of the old value is 1,
+ * or if at least one (but not all) removed bit was 0.
+ */
+ if (nsz > osz &&
+ (nv->v_quad & qbmasks[osz - 1]) != 0 &&
+ (nv->v_quad & xmask) != xmask) {
+ /*
+ * extra bits set to 0 in conversion
+ * of '%s' to '%s', op %s
+ */
+ warning(309, tyname(gettyp(ot)),
+ tyname(tp), modtab[op].m_name);
+ } else if (nsz < osz &&
+ (v->v_quad & xmask) != xmask &&
+ (v->v_quad & xmask) != 0) {
+ /* const. truncated by conv., op %s */
+ warning(306, modtab[op].m_name);
+ }
+ } else if ((nt != PTR && isutyp(nt)) &&
+ (ot != PTR && !isutyp(ot)) && v->v_quad < 0) {
+ if (op == ASSIGN) {
+ /* assignment of negative constant to ... */
+ warning(164);
+ } else if (op == INIT) {
+ /* initialisation of unsigned with neg. ... */
+ warning(221);
+ } else if (op == FARG) {
+ /* conversion of neg. const. to ..., arg #%d */
+ warning(296, arg);
+ } else if (modtab[op].m_comp) {
+ /* we get this warning already in chkcomp() */
+ } else {
+ /* conversion of negative constant to ... */
+ warning(222);
+ }
+ } else if (nv->v_quad != v->v_quad && nsz <= osz &&
+ (v->v_quad & xmask) != 0 &&
+ (isutyp(ot) || (v->v_quad & xmsk1) != xmsk1)) {
+ /*
+ * Loss of significant bit(s). All truncated bits
+ * of unsigned types or all truncated bits plus the
+ * msb of the target for signed types are considered
+ * to be significant bits. Loss of significant bits
+ * means that at least on of the bits was set in an
+ * unsigned type or that at least one, but not all of
+ * the bits was set in an signed type.
+ * Loss of significant bits means that it is not
+ * possible, also not with necessary casts, to convert
+ * back to the original type. A example for a
+ * necessary cast is:
+ * char c; int i; c = 128;
+ * i = c; ** yields -128 **
+ * i = (unsigned char)c; ** yields 128 **
+ */
+ if (op == ASSIGN && tp->t_isfield) {
+ /* precision lost in bit-field assignment */
+ warning(166);
+ } else if (op == ASSIGN) {
+ /* constant truncated by assignment */
+ warning(165);
+ } else if (op == INIT && tp->t_isfield) {
+ /* bit-field initializer does not fit */
+ warning(180);
+ } else if (op == INIT) {
+ /* initializer does not fit */
+ warning(178);
+ } else if (op == CASE) {
+ /* case label affected by conversion */
+ warning(196);
+ } else if (op == FARG) {
+ /* conv. of %s to %s is out of rng., arg #%d */
+ warning(295, tyname(gettyp(ot)), tyname(tp),
+ arg);
+ } else {
+ /* conversion of %s to %s is out of range */
+ warning(119, tyname(gettyp(ot)), tyname(tp));
+ }
+ } else if (nv->v_quad != v->v_quad) {
+ if (op == ASSIGN && tp->t_isfield) {
+ /* precision lost in bit-field assignment */
+ warning(166);
+ } else if (op == INIT && tp->t_isfield) {
+ /* bit-field initializer out of range */
+ warning(11);
+ } else if (op == CASE) {
+ /* case label affected by conversion */
+ warning(196);
+ } else if (op == FARG) {
+ /* conv. of %s to %s is out of rng., arg #%d */
+ warning(295, tyname(gettyp(ot)), tyname(tp),
+ arg);
+ } else {
+ /* conversion of %s to %s is out of range */
+ warning(119, tyname(gettyp(ot)), tyname(tp));
+ }
+ }
+ }
+}
+
+/*
+ * Called if incompatible types were detected.
+ * Prints a appropriate warning.
+ */
+static void
+incompat(op, lt, rt)
+ op_t op;
+ tspec_t lt, rt;
+{
+ mod_t *mp;
+
+ mp = &modtab[op];
+
+ if (lt == VOID || (mp->m_binary && rt == VOID)) {
+ /* void type illegal in expression */
+ error(109);
+ } else if (op == ASSIGN) {
+ if ((lt == STRUCT || lt == UNION) &&
+ (rt == STRUCT || rt == UNION)) {
+ /* assignment of different structures */
+ error(240);
+ } else {
+ /* assignment type mismatch */
+ error(171);
+ }
+ } else if (mp->m_binary) {
+ /* operands of %s have incompatible types */
+ error(107, mp->m_name);
+ } else {
+ /* operand of %s has incompatible type */
+ error(108, mp->m_name);
+ }
+}
+
+/*
+ * Called if incompatible pointer types are detected.
+ * Print an appropriate warning.
+ */
+static void
+illptrc(mp, ltp, rtp)
+ mod_t *mp;
+ type_t *ltp, *rtp;
+{
+ tspec_t lt, rt;
+
+ if (ltp->t_tspec != PTR || rtp->t_tspec != PTR)
+ lerror("illptrc() 1");
+
+ lt = ltp->t_subt->t_tspec;
+ rt = rtp->t_subt->t_tspec;
+
+ if ((lt == STRUCT || lt == UNION) && (rt == STRUCT || rt == UNION)) {
+ if (mp == NULL) {
+ /* illegal structure pointer combination */
+ warning(244);
+ } else {
+ /* illegal structure pointer combination, op %s */
+ warning(245, mp->m_name);
+ }
+ } else {
+ if (mp == NULL) {
+ /* illegal pointer combination */
+ warning(184);
+ } else {
+ /* illegal pointer combination, op %s */
+ warning(124, mp->m_name);
+ }
+ }
+}
+
+/*
+ * Make sure type (*tpp)->t_subt has at least the qualifiers
+ * of tp1->t_subt and tp2->t_subt.
+ */
+static void
+mrgqual(tpp, tp1, tp2)
+ type_t **tpp, *tp1, *tp2;
+{
+ if ((*tpp)->t_tspec != PTR ||
+ tp1->t_tspec != PTR || tp2->t_tspec != PTR) {
+ lerror("mrgqual()");
+ }
+
+ if ((*tpp)->t_subt->t_const ==
+ (tp1->t_subt->t_const | tp2->t_subt->t_const) &&
+ (*tpp)->t_subt->t_volatile ==
+ (tp1->t_subt->t_volatile | tp2->t_subt->t_volatile)) {
+ return;
+ }
+
+ *tpp = tduptyp(*tpp);
+ (*tpp)->t_subt = tduptyp((*tpp)->t_subt);
+ (*tpp)->t_subt->t_const =
+ tp1->t_subt->t_const | tp2->t_subt->t_const;
+ (*tpp)->t_subt->t_volatile =
+ tp1->t_subt->t_volatile | tp2->t_subt->t_volatile;
+}
+
+/*
+ * Returns 1 if the given structure or union has a constant member
+ * (maybe recursively).
+ */
+static int
+conmemb(tp)
+ type_t *tp;
+{
+ sym_t *m;
+ tspec_t t;
+
+ if ((t = tp->t_tspec) != STRUCT && t != UNION)
+ lerror("conmemb()");
+ for (m = tp->t_str->memb; m != NULL; m = m->s_nxt) {
+ tp = m->s_type;
+ if (tp->t_const)
+ return (1);
+ if ((t = tp->t_tspec) == STRUCT || t == UNION) {
+ if (conmemb(m->s_type))
+ return (1);
+ }
+ }
+ return (0);
+}
+
+const char *
+tyname(tp)
+ type_t *tp;
+{
+ tspec_t t;
+ const char *s;
+
+ if ((t = tp->t_tspec) == INT && tp->t_isenum)
+ t = ENUM;
+
+ switch (t) {
+ case CHAR: s = "char"; break;
+ case UCHAR: s = "unsigned char"; break;
+ case SCHAR: s = "signed char"; break;
+ case SHORT: s = "short"; break;
+ case USHORT: s = "unsigned short"; break;
+ case INT: s = "int"; break;
+ case UINT: s = "unsigned int"; break;
+ case LONG: s = "long"; break;
+ case ULONG: s = "unsigned long"; break;
+ case QUAD: s = "long long"; break;
+ case UQUAD: s = "unsigned long long"; break;
+ case FLOAT: s = "float"; break;
+ case DOUBLE: s = "double"; break;
+ case LDOUBLE: s = "long double"; break;
+ case PTR: s = "pointer"; break;
+ case ENUM: s = "enum"; break;
+ case STRUCT: s = "struct"; break;
+ case UNION: s = "union"; break;
+ case FUNC: s = "function"; break;
+ case ARRAY: s = "array"; break;
+ default:
+ lerror("tyname()");
+ }
+ return (s);
+}
+
+/*
+ * Create a new node for one of the operators POINT and ARROW.
+ */
+static tnode_t *
+bldstr(op, ln, rn)
+ op_t op;
+ tnode_t *ln, *rn;
+{
+ tnode_t *ntn, *ctn;
+ int nolval;
+
+ if (rn->tn_op != NAME)
+ lerror("bldstr() 1");
+ if (rn->tn_sym->s_value.v_tspec != INT)
+ lerror("bldstr() 2");
+ if (rn->tn_sym->s_scl != MOS && rn->tn_sym->s_scl != MOU)
+ lerror("bldstr() 3");
+
+ /*
+ * Remember if the left operand is an lvalue (structure members
+ * are lvalues if and only if the structure itself is an lvalue).
+ */
+ nolval = op == POINT && !ln->tn_lvalue;
+
+ if (op == POINT) {
+ ln = bldamper(ln, 1);
+ } else if (ln->tn_type->t_tspec != PTR) {
+ if (!tflag || !isityp(ln->tn_type->t_tspec))
+ lerror("bldstr() 4");
+ ln = convert(NOOP, 0, tincref(gettyp(VOID), PTR), ln);
+ }
+
+#if PTRDIFF_IS_LONG
+ ctn = getinode(LONG, rn->tn_sym->s_value.v_quad / CHAR_BIT);
+#else
+ ctn = getinode(INT, rn->tn_sym->s_value.v_quad / CHAR_BIT);
+#endif
+
+ ntn = mktnode(PLUS, tincref(rn->tn_type, PTR), ln, ctn);
+ if (ln->tn_op == CON)
+ ntn = fold(ntn);
+
+ if (rn->tn_type->t_isfield) {
+ ntn = mktnode(FSEL, ntn->tn_type->t_subt, ntn, NULL);
+ } else {
+ ntn = mktnode(STAR, ntn->tn_type->t_subt, ntn, NULL);
+ }
+
+ if (nolval)
+ ntn->tn_lvalue = 0;
+
+ return (ntn);
+}
+
+/*
+ * Create a node for INCAFT, INCBEF, DECAFT and DECBEF.
+ */
+static tnode_t *
+bldincdec(op, ln)
+ op_t op;
+ tnode_t *ln;
+{
+ tnode_t *cn, *ntn;
+
+ if (ln == NULL)
+ lerror("bldincdec() 1");
+
+ if (ln->tn_type->t_tspec == PTR) {
+ cn = plength(ln->tn_type);
+ } else {
+ cn = getinode(INT, (quad_t)1);
+ }
+ ntn = mktnode(op, ln->tn_type, ln, cn);
+
+ return (ntn);
+}
+
+/*
+ * Create a tree node for the & operator
+ */
+static tnode_t *
+bldamper(tn, noign)
+ tnode_t *tn;
+ int noign;
+{
+ tnode_t *ntn;
+ tspec_t t;
+
+ if (!noign && ((t = tn->tn_type->t_tspec) == ARRAY || t == FUNC)) {
+ /* & before array or function: ignored */
+ if (tflag)
+ warning(127);
+ return (tn);
+ }
+
+ /* eliminate &* */
+ if (tn->tn_op == STAR &&
+ tn->tn_left->tn_type->t_tspec == PTR &&
+ tn->tn_left->tn_type->t_subt == tn->tn_type) {
+ return (tn->tn_left);
+ }
+
+ ntn = mktnode(AMPER, tincref(tn->tn_type, PTR), tn, NULL);
+
+ return (ntn);
+}
+
+/*
+ * Create a node for operators PLUS and MINUS.
+ */
+static tnode_t *
+bldplmi(op, ln, rn)
+ op_t op;
+ tnode_t *ln, *rn;
+{
+ tnode_t *ntn, *ctn;
+ type_t *tp;
+
+ /* If pointer and integer, then pointer to the lhs. */
+ if (rn->tn_type->t_tspec == PTR && isityp(ln->tn_type->t_tspec)) {
+ ntn = ln;
+ ln = rn;
+ rn = ntn;
+ }
+
+ if (ln->tn_type->t_tspec == PTR && rn->tn_type->t_tspec != PTR) {
+
+ if (!isityp(rn->tn_type->t_tspec))
+ lerror("bldplmi() 1");
+
+ ctn = plength(ln->tn_type);
+ if (rn->tn_type->t_tspec != ctn->tn_type->t_tspec)
+ rn = convert(NOOP, 0, ctn->tn_type, rn);
+ rn = mktnode(MULT, rn->tn_type, rn, ctn);
+ if (rn->tn_left->tn_op == CON)
+ rn = fold(rn);
+ ntn = mktnode(op, ln->tn_type, ln, rn);
+
+ } else if (rn->tn_type->t_tspec == PTR) {
+
+ if (ln->tn_type->t_tspec != PTR || op != MINUS)
+ lerror("bldplmi() 2");
+#if PTRDIFF_IS_LONG
+ tp = gettyp(LONG);
+#else
+ tp = gettyp(INT);
+#endif
+ ntn = mktnode(op, tp, ln, rn);
+ if (ln->tn_op == CON && rn->tn_op == CON)
+ ntn = fold(ntn);
+ ctn = plength(ln->tn_type);
+ balance(NOOP, &ntn, &ctn);
+ ntn = mktnode(DIV, tp, ntn, ctn);
+
+ } else {
+
+ ntn = mktnode(op, ln->tn_type, ln, rn);
+
+ }
+ return (ntn);
+}
+
+/*
+ * Create a node for operators SHL and SHR.
+ */
+static tnode_t *
+bldshft(op, ln, rn)
+ op_t op;
+ tnode_t *ln, *rn;
+{
+ tspec_t t;
+ tnode_t *ntn;
+
+ if ((t = rn->tn_type->t_tspec) != INT && t != UINT)
+ rn = convert(CVT, 0, gettyp(INT), rn);
+ ntn = mktnode(op, ln->tn_type, ln, rn);
+ return (ntn);
+}
+
+/*
+ * Create a node for COLON.
+ */
+static tnode_t *
+bldcol(ln, rn)
+ tnode_t *ln, *rn;
+{
+ tspec_t lt, rt, pdt;
+ type_t *rtp;
+ tnode_t *ntn;
+
+ lt = ln->tn_type->t_tspec;
+ rt = rn->tn_type->t_tspec;
+#if PTRDIFF_IS_LONG
+ pdt = LONG;
+#else
+ pdt = INT;
+#endif
+
+ /*
+ * Arithmetic types are balanced, all other type combinations
+ * still need to be handled.
+ */
+ if (isatyp(lt) && isatyp(rt)) {
+ rtp = ln->tn_type;
+ } else if (lt == VOID || rt == VOID) {
+ rtp = gettyp(VOID);
+ } else if (lt == STRUCT || lt == UNION) {
+ /* Both types must be identical. */
+ if (rt != STRUCT && rt != UNION)
+ lerror("bldcol() 1");
+ if (ln->tn_type->t_str != rn->tn_type->t_str)
+ lerror("bldcol() 2");
+ if (incompl(ln->tn_type)) {
+ /* unknown operand size, op %s */
+ error(138, modtab[COLON].m_name);
+ return (NULL);
+ }
+ rtp = ln->tn_type;
+ } else if (lt == PTR && isityp(rt)) {
+ if (rt != pdt) {
+ rn = convert(NOOP, 0, gettyp(pdt), rn);
+ rt = pdt;
+ }
+ rtp = ln->tn_type;
+ } else if (rt == PTR && isityp(lt)) {
+ if (lt != pdt) {
+ ln = convert(NOOP, 0, gettyp(pdt), ln);
+ lt = pdt;
+ }
+ rtp = rn->tn_type;
+ } else if (lt == PTR && ln->tn_type->t_subt->t_tspec == VOID) {
+ if (rt != PTR)
+ lerror("bldcol() 4");
+ rtp = ln->tn_type;
+ mrgqual(&rtp, ln->tn_type, rn->tn_type);
+ } else if (rt == PTR && rn->tn_type->t_subt->t_tspec == VOID) {
+ if (lt != PTR)
+ lerror("bldcol() 5");
+ rtp = rn->tn_type;
+ mrgqual(&rtp, ln->tn_type, rn->tn_type);
+ } else {
+ if (lt != PTR || rt != PTR)
+ lerror("bldcol() 6");
+ /*
+ * XXX For now we simply take the left type. This is
+ * probably wrong, if one type contains a functionprototype
+ * and the other one, at the same place, only an old style
+ * declaration.
+ */
+ rtp = ln->tn_type;
+ mrgqual(&rtp, ln->tn_type, rn->tn_type);
+ }
+
+ ntn = mktnode(COLON, rtp, ln, rn);
+
+ return (ntn);
+}
+
+/*
+ * Create a node for an assignment operator (both = and op= ).
+ */
+static tnode_t *
+bldasgn(op, ln, rn)
+ op_t op;
+ tnode_t *ln, *rn;
+{
+ tspec_t lt, rt;
+ tnode_t *ntn, *ctn;
+
+ if (ln == NULL || rn == NULL)
+ lerror("bldasgn() 1");
+
+ lt = ln->tn_type->t_tspec;
+ rt = rn->tn_type->t_tspec;
+
+ if ((op == ADDASS || op == SUBASS) && lt == PTR) {
+ if (!isityp(rt))
+ lerror("bldasgn() 2");
+ ctn = plength(ln->tn_type);
+ if (rn->tn_type->t_tspec != ctn->tn_type->t_tspec)
+ rn = convert(NOOP, 0, ctn->tn_type, rn);
+ rn = mktnode(MULT, rn->tn_type, rn, ctn);
+ if (rn->tn_left->tn_op == CON)
+ rn = fold(rn);
+ }
+
+ if ((op == ASSIGN || op == RETURN) && (lt == STRUCT || rt == STRUCT)) {
+ if (rt != lt || ln->tn_type->t_str != rn->tn_type->t_str)
+ lerror("bldasgn() 3");
+ if (incompl(ln->tn_type)) {
+ if (op == RETURN) {
+ /* cannot return incomplete type */
+ error(212);
+ } else {
+ /* unknown operand size, op %s */
+ error(138, modtab[op].m_name);
+ }
+ return (NULL);
+ }
+ }
+
+ if (op == SHLASS || op == SHRASS) {
+ if (rt != INT) {
+ rn = convert(NOOP, 0, gettyp(INT), rn);
+ rt = INT;
+ }
+ } else {
+ if (op == ASSIGN || lt != PTR) {
+ if (lt != rt ||
+ (ln->tn_type->t_isfield && rn->tn_op == CON)) {
+ rn = convert(op, 0, ln->tn_type, rn);
+ rt = lt;
+ }
+ }
+ }
+
+ ntn = mktnode(op, ln->tn_type, ln, rn);
+
+ return (ntn);
+}
+
+/*
+ * Get length of type tp->t_subt.
+ */
+static tnode_t *
+plength(tp)
+ type_t *tp;
+{
+ int elem, elsz;
+ tspec_t st;
+
+ if (tp->t_tspec != PTR)
+ lerror("plength() 1");
+ tp = tp->t_subt;
+
+ elem = 1;
+ elsz = 0;
+
+ while (tp->t_tspec == ARRAY) {
+ elem *= tp->t_dim;
+ tp = tp->t_subt;
+ }
+
+ switch (tp->t_tspec) {
+ case FUNC:
+ /* pointer to function is not allowed here */
+ error(110);
+ break;
+ case VOID:
+ /* cannot do pointer arithmetic on operand of ... */
+ (void)gnuism(136);
+ break;
+ case STRUCT:
+ case UNION:
+ if ((elsz = tp->t_str->size) == 0)
+ /* cannot do pointer arithmetic on operand of ... */
+ error(136);
+ break;
+ case ENUM:
+ if (incompl(tp)) {
+ /* cannot do pointer arithmetic on operand of ... */
+ warning(136);
+ }
+ /* FALLTHROUGH */
+ default:
+ if ((elsz = size(tp->t_tspec)) == 0) {
+ /* cannot do pointer arithmetic on operand of ... */
+ error(136);
+ } else if (elsz == -1) {
+ lerror("plength() 2");
+ }
+ break;
+ }
+
+ if (elem == 0 && elsz != 0) {
+ /* cannot do pointer arithmetic on operand of ... */
+ error(136);
+ }
+
+ if (elsz == 0)
+ elsz = CHAR_BIT;
+
+#if PTRDIFF_IS_LONG
+ st = LONG;
+#else
+ st = INT;
+#endif
+
+ return (getinode(st, (quad_t)(elem * elsz / CHAR_BIT)));
+}
+
+#ifdef XXX_BROKEN_GCC
+static int
+quad_t_eq(x, y)
+ quad_t x, y;
+{
+ return (x == y);
+}
+
+static int
+u_quad_t_eq(x, y)
+ u_quad_t x, y;
+{
+ return (x == y);
+}
+#endif
+
+/*
+ * Do only as much as necessary to compute constant expressions.
+ * Called only if the operator allows folding and (both) operands
+ * are constants.
+ */
+static tnode_t *
+fold(tn)
+ tnode_t *tn;
+{
+ val_t *v;
+ tspec_t t;
+ int utyp, ovfl;
+ quad_t sl, sr, q, mask;
+ u_quad_t ul, ur;
+ tnode_t *cn;
+
+ v = xcalloc(1, sizeof (val_t));
+ v->v_tspec = t = tn->tn_type->t_tspec;
+
+ utyp = t == PTR || isutyp(t);
+ ul = sl = tn->tn_left->tn_val->v_quad;
+ if (modtab[tn->tn_op].m_binary)
+ ur = sr = tn->tn_right->tn_val->v_quad;
+
+ ovfl = 0;
+
+ switch (tn->tn_op) {
+ case UPLUS:
+ q = sl;
+ break;
+ case UMINUS:
+ q = -sl;
+ if (msb(q, t, -1) == msb(sl, t, -1))
+ ovfl = 1;
+ break;
+ case COMPL:
+ q = ~sl;
+ break;
+ case MULT:
+ q = utyp ? ul * ur : sl * sr;
+ if (msb(q, t, -1) != (msb(sl, t, -1) ^ msb(sr, t, -1)))
+ ovfl = 1;
+ break;
+ case DIV:
+ if (sr == 0) {
+ /* division by 0 */
+ error(139);
+ q = utyp ? UQUAD_MAX : QUAD_MAX;
+ } else {
+ q = utyp ? ul / ur : sl / sr;
+ }
+ break;
+ case MOD:
+ if (sr == 0) {
+ /* modulus by 0 */
+ error(140);
+ q = 0;
+ } else {
+ q = utyp ? ul % ur : sl % sr;
+ }
+ break;
+ case PLUS:
+ q = utyp ? ul + ur : sl + sr;
+ if (msb(sl, t, -1) != 0 && msb(sr, t, -1) != 0) {
+ if (msb(q, t, -1) == 0)
+ ovfl = 1;
+ } else if (msb(sl, t, -1) == 0 && msb(sr, t, -1) == 0) {
+ if (msb(q, t, -1) != 0)
+ ovfl = 1;
+ }
+ break;
+ case MINUS:
+ q = utyp ? ul - ur : sl - sr;
+ if (msb(sl, t, -1) != 0 && msb(sr, t, -1) == 0) {
+ if (msb(q, t, -1) == 0)
+ ovfl = 1;
+ } else if (msb(sl, t, -1) == 0 && msb(sr, t, -1) != 0) {
+ if (msb(q, t, -1) != 0)
+ ovfl = 1;
+ }
+ break;
+ case SHL:
+ q = utyp ? ul << sr : sl << sr;
+ break;
+ case SHR:
+ /*
+ * The sign must be explizitly extended because
+ * shifts of signed values are implementation dependent.
+ */
+ q = ul >> sr;
+ q = xsign(q, t, size(t) - (int)sr);
+ break;
+ case LT:
+ q = utyp ? ul < ur : sl < sr;
+ break;
+ case LE:
+ q = utyp ? ul <= ur : sl <= sr;
+ break;
+ case GE:
+ q = utyp ? ul >= ur : sl >= sr;
+ break;
+ case GT:
+ q = utyp ? ul > ur : sl > sr;
+ break;
+ case EQ:
+#ifdef XXX_BROKEN_GCC
+ q = utyp ? u_quad_t_eq(ul, ur) : quad_t_eq(sl, sr);
+#else
+ q = utyp ? ul == ur : sl == sr;
+#endif
+ break;
+ case NE:
+ q = utyp ? ul != ur : sl != sr;
+ break;
+ case AND:
+ q = utyp ? ul & ur : sl & sr;
+ break;
+ case XOR:
+ q = utyp ? ul ^ ur : sl ^ sr;
+ break;
+ case OR:
+ q = utyp ? ul | ur : sl | sr;
+ break;
+ default:
+ lerror("fold() 5");
+ }
+
+ mask = qlmasks[size(t)];
+
+ /* XXX does not work for quads. */
+ if (ovfl || ((q | mask) != ~(u_quad_t)0 && (q & ~mask) != 0)) {
+ if (hflag)
+ /* integer overflow detected, op %s */
+ warning(141, modtab[tn->tn_op].m_name);
+ }
+
+ v->v_quad = xsign(q, t, -1);
+
+ cn = getcnode(tn->tn_type, v);
+
+ return (cn);
+}
+
+#ifdef XXX_BROKEN_GCC
+int
+ldbl_t_neq(x, y)
+ ldbl_t x, y;
+{
+ return (x != y);
+}
+#endif
+
+/*
+ * Same for operators whose operands are compared with 0 (test context).
+ */
+static tnode_t *
+foldtst(tn)
+ tnode_t *tn;
+{
+ int l, r;
+ val_t *v;
+
+ v = xcalloc(1, sizeof (val_t));
+ v->v_tspec = tn->tn_type->t_tspec;
+ if (tn->tn_type->t_tspec != INT)
+ lerror("foldtst() 1");
+
+ if (isftyp(tn->tn_left->tn_type->t_tspec)) {
+#ifdef XXX_BROKEN_GCC
+ l = ldbl_t_neq(tn->tn_left->tn_val->v_ldbl, 0.0);
+#else
+ l = tn->tn_left->tn_val->v_ldbl != 0.0;
+#endif
+ } else {
+ l = tn->tn_left->tn_val->v_quad != 0;
+ }
+
+ if (modtab[tn->tn_op].m_binary) {
+ if (isftyp(tn->tn_right->tn_type->t_tspec)) {
+#ifdef XXX_BROKEN_GCC
+ r = ldbl_t_neq(tn->tn_right->tn_val->v_ldbl, 0.0);
+#else
+ r = tn->tn_right->tn_val->v_ldbl != 0.0;
+#endif
+ } else {
+ r = tn->tn_right->tn_val->v_quad != 0;
+ }
+ }
+
+ switch (tn->tn_op) {
+ case NOT:
+ if (hflag)
+ /* constant argument to NOT */
+ warning(239);
+ v->v_quad = !l;
+ break;
+ case LOGAND:
+ v->v_quad = l && r;
+ break;
+ case LOGOR:
+ v->v_quad = l || r;
+ break;
+ default:
+ lerror("foldtst() 1");
+ }
+
+ return (getcnode(tn->tn_type, v));
+}
+
+/*
+ * Same for operands with floating point type.
+ */
+static tnode_t *
+foldflt(tn)
+ tnode_t *tn;
+{
+ val_t *v;
+ tspec_t t;
+ ldbl_t l, r;
+
+ v = xcalloc(1, sizeof (val_t));
+ v->v_tspec = t = tn->tn_type->t_tspec;
+
+ if (!isftyp(t))
+ lerror("foldflt() 1");
+
+ if (t != tn->tn_left->tn_type->t_tspec)
+ lerror("foldflt() 2");
+ if (modtab[tn->tn_op].m_binary && t != tn->tn_right->tn_type->t_tspec)
+ lerror("foldflt() 3");
+
+ l = tn->tn_left->tn_val->v_ldbl;
+ if (modtab[tn->tn_op].m_binary)
+ r = tn->tn_right->tn_val->v_ldbl;
+
+ switch (tn->tn_op) {
+ case UPLUS:
+ v->v_ldbl = l;
+ break;
+ case UMINUS:
+ v->v_ldbl = -l;
+ break;
+ case MULT:
+ v->v_ldbl = l * r;
+ break;
+ case DIV:
+ if (r == 0.0) {
+ /* division by 0 */
+ error(139);
+ if (t == FLOAT) {
+ v->v_ldbl = l < 0 ? -FLT_MAX : FLT_MAX;
+ } else if (t == DOUBLE) {
+ v->v_ldbl = l < 0 ? -DBL_MAX : DBL_MAX;
+ } else {
+ v->v_ldbl = l < 0 ? -LDBL_MAX : LDBL_MAX;
+ }
+ } else {
+ v->v_ldbl = l / r;
+ }
+ break;
+ case PLUS:
+ v->v_ldbl = l + r;
+ break;
+ case MINUS:
+ v->v_ldbl = l - r;
+ break;
+ case LT:
+ v->v_quad = l < r;
+ break;
+ case LE:
+ v->v_quad = l <= r;
+ break;
+ case GE:
+ v->v_quad = l >= r;
+ break;
+ case GT:
+ v->v_quad = l > r;
+ break;
+ case EQ:
+ v->v_quad = l == r;
+ break;
+ case NE:
+ v->v_quad = l != r;
+ break;
+ default:
+ lerror("foldflt() 4");
+ }
+
+ if (isnan((double)v->v_ldbl))
+ lerror("foldflt() 5");
+ if (isinf((double)v->v_ldbl) ||
+ (t == FLOAT &&
+ (v->v_ldbl > FLT_MAX || v->v_ldbl < -FLT_MAX)) ||
+ (t == DOUBLE &&
+ (v->v_ldbl > DBL_MAX || v->v_ldbl < -DBL_MAX))) {
+ /* floating point overflow detected, op %s */
+ warning(142, modtab[tn->tn_op].m_name);
+ if (t == FLOAT) {
+ v->v_ldbl = v->v_ldbl < 0 ? -FLT_MAX : FLT_MAX;
+ } else if (t == DOUBLE) {
+ v->v_ldbl = v->v_ldbl < 0 ? -DBL_MAX : DBL_MAX;
+ } else {
+ v->v_ldbl = v->v_ldbl < 0 ? -LDBL_MAX: LDBL_MAX;
+ }
+ }
+
+ return (getcnode(tn->tn_type, v));
+}
+
+/*
+ * Create a constant node for sizeof.
+ */
+tnode_t *
+bldszof(tp)
+ type_t *tp;
+{
+ int elem, elsz;
+ tspec_t st;
+
+ elem = 1;
+ while (tp->t_tspec == ARRAY) {
+ elem *= tp->t_dim;
+ tp = tp->t_subt;
+ }
+ if (elem == 0) {
+ /* cannot take size of incomplete type */
+ error(143);
+ elem = 1;
+ }
+ switch (tp->t_tspec) {
+ case FUNC:
+ /* cannot take size of function */
+ error(144);
+ elsz = 1;
+ break;
+ case STRUCT:
+ case UNION:
+ if (incompl(tp)) {
+ /* cannot take size of incomplete type */
+ error(143);
+ elsz = 1;
+ } else {
+ elsz = tp->t_str->size;
+ }
+ break;
+ case ENUM:
+ if (incompl(tp)) {
+ /* cannot take size of incomplete type */
+ warning(143);
+ }
+ /* FALLTHROUGH */
+ default:
+ if (tp->t_isfield) {
+ /* cannot take size of bit-field */
+ error(145);
+ }
+ if (tp->t_tspec == VOID) {
+ /* cannot take size of void */
+ error(146);
+ elsz = 1;
+ } else {
+ elsz = size(tp->t_tspec);
+ if (elsz <= 0)
+ lerror("bldszof() 1");
+ }
+ break;
+ }
+
+#if SIZEOF_IS_ULONG
+ st = ULONG;
+#else
+ st = UINT;
+#endif
+
+ return (getinode(st, (quad_t)(elem * elsz / CHAR_BIT)));
+}
+
+/*
+ * Type casts.
+ */
+tnode_t *
+cast(tn, tp)
+ tnode_t *tn;
+ type_t *tp;
+{
+ tspec_t nt, ot;
+
+ if (tn == NULL)
+ return (NULL);
+
+ tn = cconv(tn);
+
+ nt = tp->t_tspec;
+ ot = tn->tn_type->t_tspec;
+
+ if (nt == VOID) {
+ /*
+ * XXX ANSI C requires scalar types or void (Plauger&Brodie).
+ * But this seams really questionable.
+ */
+ } else if (nt == STRUCT || nt == UNION || nt == ARRAY || nt == FUNC) {
+ /* invalid cast expression */
+ error(147);
+ return (NULL);
+ } else if (ot == STRUCT || ot == UNION) {
+ /* invalid cast expression */
+ error(147);
+ return (NULL);
+ } else if (ot == VOID) {
+ /* improper cast of void expression */
+ error(148);
+ return (NULL);
+ } else if (isityp(nt) && issclt(ot)) {
+ /* ok */
+ } else if (isftyp(nt) && isatyp(ot)) {
+ /* ok */
+ } else if (nt == PTR && isityp(ot)) {
+ /* ok */
+ } else if (nt == PTR && ot == PTR) {
+ if (!tp->t_subt->t_const && tn->tn_type->t_subt->t_const) {
+ if (hflag)
+ /* cast discards 'const' from ... */
+ warning(275);
+ }
+ } else {
+ /* invalid cast expression */
+ error(147);
+ return (NULL);
+ }
+
+ tn = convert(CVT, 0, tp, tn);
+ tn->tn_cast = 1;
+
+ return (tn);
+}
+
+/*
+ * Create the node for a function argument.
+ * All necessary conversions and type checks are done in funccall(), because
+ * in funcarg() we have no information about expected argument types.
+ */
+tnode_t *
+funcarg(args, arg)
+ tnode_t *args, *arg;
+{
+ tnode_t *ntn;
+
+ /*
+ * If there was a serious error in the expression for the argument,
+ * create a dummy argument so the positions of the remaining arguments
+ * will not change.
+ */
+ if (arg == NULL)
+ arg = getinode(INT, (quad_t)0);
+
+ ntn = mktnode(PUSH, arg->tn_type, arg, args);
+
+ return (ntn);
+}
+
+/*
+ * Create the node for a function call. Also check types of
+ * function arguments and insert conversions, if necessary.
+ */
+tnode_t *
+funccall(func, args)
+ tnode_t *func, *args;
+{
+ tnode_t *ntn;
+ op_t fcop;
+
+ if (func == NULL)
+ return (NULL);
+
+ if (func->tn_op == NAME && func->tn_type->t_tspec == FUNC) {
+ fcop = CALL;
+ } else {
+ fcop = ICALL;
+ }
+
+ /*
+ * after cconv() func will always be a pointer to a function
+ * if it is a valid function designator.
+ */
+ func = cconv(func);
+
+ if (func->tn_type->t_tspec != PTR ||
+ func->tn_type->t_subt->t_tspec != FUNC) {
+ /* illegal function */
+ error(149);
+ return (NULL);
+ }
+
+ args = chkfarg(func->tn_type->t_subt, args);
+
+ ntn = mktnode(fcop, func->tn_type->t_subt->t_subt, func, args);
+
+ return (ntn);
+}
+
+/*
+ * Check types of all function arguments and insert conversions,
+ * if necessary.
+ */
+static tnode_t *
+chkfarg(ftp, args)
+ type_t *ftp; /* type of called function */
+ tnode_t *args; /* arguments */
+{
+ tnode_t *arg;
+ sym_t *asym;
+ tspec_t at;
+ int narg, npar, n, i;
+
+ /* get # of args in the prototype */
+ npar = 0;
+ for (asym = ftp->t_args; asym != NULL; asym = asym->s_nxt)
+ npar++;
+
+ /* get # of args in function call */
+ narg = 0;
+ for (arg = args; arg != NULL; arg = arg->tn_right)
+ narg++;
+
+ asym = ftp->t_args;
+ if (ftp->t_proto && npar != narg && !(ftp->t_vararg && npar < narg)) {
+ /* argument mismatch: %d arg%s passed, %d expected */
+ error(150, narg, narg > 1 ? "s" : "", npar);
+ asym = NULL;
+ }
+
+ for (n = 1; n <= narg; n++) {
+
+ /*
+ * The rightmost argument is at the top of the argument
+ * subtree.
+ */
+ for (i = narg, arg = args; i > n; i--, arg = arg->tn_right) ;
+
+ /* some things which are always not allowd */
+ if ((at = arg->tn_left->tn_type->t_tspec) == VOID) {
+ /* void expressions may not be arguments, arg #%d */
+ error(151, n);
+ return (NULL);
+ } else if ((at == STRUCT || at == UNION) &&
+ incompl(arg->tn_left->tn_type)) {
+ /* argument cannot have unknown size, arg #%d */
+ error(152, n);
+ return (NULL);
+ } else if (isityp(at) && arg->tn_left->tn_type->t_isenum &&
+ incompl(arg->tn_left->tn_type)) {
+ /* argument cannot have unknown size, arg #%d */
+ warning(152, n);
+ }
+
+ /* class conversions (arg in value context) */
+ arg->tn_left = cconv(arg->tn_left);
+
+ if (asym != NULL) {
+ arg->tn_left = parg(n, asym->s_type, arg->tn_left);
+ } else {
+ arg->tn_left = promote(NOOP, 1, arg->tn_left);
+ }
+ arg->tn_type = arg->tn_left->tn_type;
+
+ if (asym != NULL)
+ asym = asym->s_nxt;
+ }
+
+ return (args);
+}
+
+/*
+ * Compare the type of an argument with the corresponding type of a
+ * prototype parameter. If it is a valid combination, but both types
+ * are not the same, insert a conversion to convert the argument into
+ * the type of the parameter.
+ */
+static tnode_t *
+parg(n, tp, tn)
+ int n; /* pos of arg */
+ type_t *tp; /* expected type (from prototype) */
+ tnode_t *tn; /* argument */
+{
+ tnode_t *ln;
+ int warn;
+
+ ln = xcalloc(1, sizeof (tnode_t));
+ ln->tn_type = tduptyp(tp);
+ ln->tn_type->t_const = 0;
+ ln->tn_lvalue = 1;
+ if (typeok(FARG, n, ln, tn)) {
+ if (!eqtype(tp, tn->tn_type, 1, 0, (warn = 0, &warn)) || warn)
+ tn = convert(FARG, n, tp, tn);
+ }
+ free(ln);
+ return (tn);
+}
+
+/*
+ * Return the value of an integral constant expression.
+ * If the expression is not constant or its type is not an integer
+ * type, an error message is printed.
+ */
+val_t *
+constant(tn)
+ tnode_t *tn;
+{
+ val_t *v;
+
+ if (tn != NULL)
+ tn = cconv(tn);
+ if (tn != NULL)
+ tn = promote(NOOP, 0, tn);
+
+ v = xcalloc(1, sizeof (val_t));
+
+ if (tn == NULL) {
+ if (nerr == 0)
+ lerror("constant() 1");
+ v->v_tspec = INT;
+ v->v_quad = 1;
+ return (v);
+ }
+
+ v->v_tspec = tn->tn_type->t_tspec;
+
+ if (tn->tn_op == CON) {
+ if (tn->tn_type->t_tspec != tn->tn_val->v_tspec)
+ lerror("constant() 2");
+ if (isityp(tn->tn_val->v_tspec)) {
+ v->v_ansiu = tn->tn_val->v_ansiu;
+ v->v_quad = tn->tn_val->v_quad;
+ return (v);
+ }
+ v->v_quad = tn->tn_val->v_ldbl;
+ } else {
+ v->v_quad = 1;
+ }
+
+ /* integral constant expression expected */
+ error(55);
+
+ if (!isityp(v->v_tspec))
+ v->v_tspec = INT;
+
+ return (v);
+}
+
+/*
+ * Perform some tests on expressions which can't be done in build() and
+ * functions called by build(). These tests must be done here because
+ * we need some information about the context in which the operations
+ * are performed.
+ * After all tests are performed, expr() frees the memory which is used
+ * for the expression.
+ */
+void
+expr(tn, vctx, tctx)
+ tnode_t *tn;
+ int vctx, tctx;
+{
+ if (tn == NULL && nerr == 0)
+ lerror("expr() 1");
+
+ if (tn == NULL) {
+ tfreeblk();
+ return;
+ }
+
+ /* expr() is also called in global initialisations */
+ if (dcs->d_ctx != EXTERN)
+ chkreach();
+
+ chkmisc(tn, vctx, tctx, !tctx, 0, 0, 0);
+ if (tn->tn_op == ASSIGN) {
+ if (hflag && tctx)
+ /* assignment in conditional context */
+ warning(159);
+ } else if (tn->tn_op == CON) {
+ if (hflag && tctx && !ccflg)
+ /* constant in conditional context */
+ warning(161);
+ }
+ if (!modtab[tn->tn_op].m_sideeff) {
+ /*
+ * for left operands of COMMA this warning is already
+ * printed
+ */
+ if (tn->tn_op != COMMA && !vctx && !tctx)
+ nulleff(tn);
+ }
+ if (dflag)
+ displexpr(tn, 0);
+
+ /* free the tree memory */
+ tfreeblk();
+}
+
+static void
+nulleff(tn)
+ tnode_t *tn;
+{
+ if (!hflag)
+ return;
+
+ while (!modtab[tn->tn_op].m_sideeff) {
+ if (tn->tn_op == CVT && tn->tn_type->t_tspec == VOID) {
+ tn = tn->tn_left;
+ } else if (tn->tn_op == LOGAND || tn->tn_op == LOGOR) {
+ /*
+ * && and || have a side effect if the right operand
+ * has a side effect.
+ */
+ tn = tn->tn_right;
+ } else if (tn->tn_op == QUEST) {
+ /*
+ * ? has a side effect if at least one of its right
+ * operands has a side effect
+ */
+ tn = tn->tn_right;
+ } else if (tn->tn_op == COLON) {
+ /*
+ * : has a side effect if at least one of its operands
+ * has a side effect
+ */
+ if (modtab[tn->tn_left->tn_op].m_sideeff) {
+ tn = tn->tn_left;
+ } else if (modtab[tn->tn_right->tn_op].m_sideeff) {
+ tn = tn->tn_right;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ if (!modtab[tn->tn_op].m_sideeff)
+ /* expression has null effect */
+ warning(129);
+}
+
+/*
+ * Dump an expression to stdout
+ * only used for debugging
+ */
+static void
+displexpr(tn, offs)
+ tnode_t *tn;
+ int offs;
+{
+ u_quad_t uq;
+
+ if (tn == NULL) {
+ (void)printf("%*s%s\n", offs, "", "NULL");
+ return;
+ }
+ (void)printf("%*sop %s ", offs, "", modtab[tn->tn_op].m_name);
+
+ if (tn->tn_op == NAME) {
+ (void)printf("%s: %s ",
+ tn->tn_sym->s_name, scltoa(tn->tn_sym->s_scl));
+ } else if (tn->tn_op == CON && isftyp(tn->tn_type->t_tspec)) {
+ (void)printf("%#g ", (double)tn->tn_val->v_ldbl);
+ } else if (tn->tn_op == CON && isityp(tn->tn_type->t_tspec)) {
+ uq = tn->tn_val->v_quad;
+ (void)printf("0x %08lx %08lx ", (long)(uq >> 32) & 0xffffffffl,
+ (long)uq & 0xffffffffl);
+ } else if (tn->tn_op == CON) {
+ if (tn->tn_type->t_tspec != PTR)
+ lerror("displexpr() 1");
+ (void)printf("0x%0*lx ", (int)(sizeof (void *) * CHAR_BIT / 4),
+ (u_long)tn->tn_val->v_quad);
+ } else if (tn->tn_op == STRING) {
+ if (tn->tn_strg->st_tspec == CHAR) {
+ (void)printf("\"%s\"", tn->tn_strg->st_cp);
+ } else {
+ char *s;
+ size_t n;
+ n = MB_CUR_MAX * (tn->tn_strg->st_len + 1);
+ s = xmalloc(n);
+ (void)wcstombs(s, tn->tn_strg->st_wcp, n);
+ (void)printf("L\"%s\"", s);
+ free(s);
+ }
+ (void)printf(" ");
+ } else if (tn->tn_op == FSEL) {
+ (void)printf("o=%d, l=%d ", tn->tn_type->t_foffs,
+ tn->tn_type->t_flen);
+ }
+ (void)printf("%s\n", ttos(tn->tn_type));
+ if (tn->tn_op == NAME || tn->tn_op == CON || tn->tn_op == STRING)
+ return;
+ displexpr(tn->tn_left, offs + 2);
+ if (modtab[tn->tn_op].m_binary ||
+ (tn->tn_op == PUSH && tn->tn_right != NULL)) {
+ displexpr(tn->tn_right, offs + 2);
+ }
+}
+
+/*
+ * Called by expr() to recursively perform some tests.
+ */
+/* ARGSUSED */
+void
+chkmisc(tn, vctx, tctx, eqwarn, fcall, rvdisc, szof)
+ tnode_t *tn;
+ int vctx, tctx, eqwarn, fcall, rvdisc, szof;
+{
+ tnode_t *ln, *rn;
+ mod_t *mp;
+ int nrvdisc, cvctx, ctctx;
+ op_t op;
+ scl_t sc;
+ dinfo_t *di;
+
+ if (tn == NULL)
+ return;
+
+ ln = tn->tn_left;
+ rn = tn->tn_right;
+ mp = &modtab[op = tn->tn_op];
+
+ switch (op) {
+ case AMPER:
+ if (ln->tn_op == NAME && (reached || rchflg)) {
+ if (!szof)
+ setsflg(ln->tn_sym);
+ setuflg(ln->tn_sym, fcall, szof);
+ }
+ if (ln->tn_op == STAR && ln->tn_left->tn_op == PLUS)
+ /* check the range of array indices */
+ chkaidx(ln->tn_left, 1);
+ break;
+ case LOAD:
+ if (ln->tn_op == STAR && ln->tn_left->tn_op == PLUS)
+ /* check the range of array indices */
+ chkaidx(ln->tn_left, 0);
+ /* FALLTHROUGH */
+ case PUSH:
+ case INCBEF:
+ case DECBEF:
+ case INCAFT:
+ case DECAFT:
+ case ADDASS:
+ case SUBASS:
+ case MULASS:
+ case DIVASS:
+ case MODASS:
+ case ANDASS:
+ case ORASS:
+ case XORASS:
+ case SHLASS:
+ case SHRASS:
+ if (ln->tn_op == NAME && (reached || rchflg)) {
+ sc = ln->tn_sym->s_scl;
+ /*
+ * Look if there was a asm statement in one of the
+ * compound statements we are in. If not, we don't
+ * print a warning.
+ */
+ for (di = dcs; di != NULL; di = di->d_nxt) {
+ if (di->d_asm)
+ break;
+ }
+ if (sc != EXTERN && sc != STATIC &&
+ !ln->tn_sym->s_set && !szof && di == NULL) {
+ /* %s may be used before set */
+ warning(158, ln->tn_sym->s_name);
+ setsflg(ln->tn_sym);
+ }
+ setuflg(ln->tn_sym, 0, 0);
+ }
+ break;
+ case ASSIGN:
+ if (ln->tn_op == NAME && !szof && (reached || rchflg)) {
+ setsflg(ln->tn_sym);
+ if (ln->tn_sym->s_scl == EXTERN)
+ outusg(ln->tn_sym);
+ }
+ if (ln->tn_op == STAR && ln->tn_left->tn_op == PLUS)
+ /* check the range of array indices */
+ chkaidx(ln->tn_left, 0);
+ break;
+ case CALL:
+ if (ln->tn_op != AMPER || ln->tn_left->tn_op != NAME)
+ lerror("chkmisc() 1");
+ if (!szof)
+ outcall(tn, vctx || tctx, rvdisc);
+ break;
+ case EQ:
+ /* equality operator "==" found where "=" was exp. */
+ if (hflag && eqwarn)
+ warning(160);
+ break;
+ case CON:
+ case NAME:
+ case STRING:
+ return;
+ /* LINTED (enumeration values not handled in switch) */
+ default:
+ }
+
+ cvctx = mp->m_vctx;
+ ctctx = mp->m_tctx;
+ /*
+ * values of operands of ':' are not used if the type of at least
+ * one of the operands (for gcc compatibility) is void
+ * XXX test/value context of QUEST should probably be used as
+ * context for both operands of COLON
+ */
+ if (op == COLON && tn->tn_type->t_tspec == VOID)
+ cvctx = ctctx = 0;
+ nrvdisc = op == CVT && tn->tn_type->t_tspec == VOID;
+ chkmisc(ln, cvctx, ctctx, mp->m_eqwarn, op == CALL, nrvdisc, szof);
+
+ switch (op) {
+ case PUSH:
+ if (rn != NULL)
+ chkmisc(rn, 0, 0, mp->m_eqwarn, 0, 0, szof);
+ break;
+ case LOGAND:
+ case LOGOR:
+ chkmisc(rn, 0, 1, mp->m_eqwarn, 0, 0, szof);
+ break;
+ case COLON:
+ chkmisc(rn, cvctx, ctctx, mp->m_eqwarn, 0, 0, szof);
+ break;
+ default:
+ if (mp->m_binary)
+ chkmisc(rn, 1, 0, mp->m_eqwarn, 0, 0, szof);
+ break;
+ }
+
+}
+
+/*
+ * Checks the range of array indices, if possible.
+ * amper is set if only the address of the element is used. This
+ * means that the index is allowd to refere to the first element
+ * after the array.
+ */
+static void
+chkaidx(tn, amper)
+ tnode_t *tn;
+ int amper;
+{
+ int dim;
+ tnode_t *ln, *rn;
+ int elsz;
+ quad_t con;
+
+ ln = tn->tn_left;
+ rn = tn->tn_right;
+
+ /* We can only check constant indices. */
+ if (rn->tn_op != CON)
+ return;
+
+ /* Return if the left node does not stem from an array. */
+ if (ln->tn_op != AMPER)
+ return;
+ if (ln->tn_left->tn_op != STRING && ln->tn_left->tn_op != NAME)
+ return;
+ if (ln->tn_left->tn_type->t_tspec != ARRAY)
+ return;
+
+ /*
+ * For incomplete array types, we can print a warning only if
+ * the index is negative.
+ */
+ if (incompl(ln->tn_left->tn_type) && rn->tn_val->v_quad >= 0)
+ return;
+
+ /* Get the size of one array element */
+ if ((elsz = length(ln->tn_type->t_subt, NULL)) == 0)
+ return;
+ elsz /= CHAR_BIT;
+
+ /* Change the unit of the index from bytes to element size. */
+ if (isutyp(rn->tn_type->t_tspec)) {
+ con = (u_quad_t)rn->tn_val->v_quad / elsz;
+ } else {
+ con = rn->tn_val->v_quad / elsz;
+ }
+
+ dim = ln->tn_left->tn_type->t_dim + (amper ? 1 : 0);
+
+ if (!isutyp(rn->tn_type->t_tspec) && con < 0) {
+ /* array subscript cannot be negative: %ld */
+ warning(167, (long)con);
+ } else if (dim > 0 && (u_quad_t)con >= dim) {
+ /* array subscript cannot be > %d: %ld */
+ warning(168, dim - 1, (long)con);
+ }
+}
+
+/*
+ * Check for ordered comparisions of unsigned values with 0.
+ */
+static void
+chkcomp(op, ln, rn)
+ op_t op;
+ tnode_t *ln, *rn;
+{
+ tspec_t lt, rt;
+ mod_t *mp;
+
+ lt = ln->tn_type->t_tspec;
+ rt = rn->tn_type->t_tspec;
+ mp = &modtab[op];
+
+ if (ln->tn_op != CON && rn->tn_op != CON)
+ return;
+
+ if (!isityp(lt) || !isityp(rt))
+ return;
+
+ if ((hflag || pflag) && lt == CHAR && rn->tn_op == CON &&
+ (rn->tn_val->v_quad < 0 ||
+ rn->tn_val->v_quad > ~(~0 << (CHAR_BIT - 1)))) {
+ /* nonportable character comparision, op %s */
+ warning(230, mp->m_name);
+ return;
+ }
+ if ((hflag || pflag) && rt == CHAR && ln->tn_op == CON &&
+ (ln->tn_val->v_quad < 0 ||
+ ln->tn_val->v_quad > ~(~0 << (CHAR_BIT - 1)))) {
+ /* nonportable character comparision, op %s */
+ warning(230, mp->m_name);
+ return;
+ }
+ if (isutyp(lt) && !isutyp(rt) &&
+ rn->tn_op == CON && rn->tn_val->v_quad <= 0) {
+ if (rn->tn_val->v_quad < 0) {
+ /* comparision of %s with %s, op %s */
+ warning(162, tyname(ln->tn_type), "negative constant",
+ mp->m_name);
+ } else if (op == LT || op == GE || (hflag && op == LE)) {
+ /* comparision of %s with %s, op %s */
+ warning(162, tyname(ln->tn_type), "0", mp->m_name);
+ }
+ return;
+ }
+ if (isutyp(rt) && !isutyp(lt) &&
+ ln->tn_op == CON && ln->tn_val->v_quad <= 0) {
+ if (ln->tn_val->v_quad < 0) {
+ /* comparision of %s with %s, op %s */
+ warning(162, "negative constant", tyname(rn->tn_type),
+ mp->m_name);
+ } else if (op == GT || op == LE || (hflag && op == GE)) {
+ /* comparision of %s with %s, op %s */
+ warning(162, "0", tyname(rn->tn_type), mp->m_name);
+ }
+ return;
+ }
+}
+
+/*
+ * Takes an expression an returns 0 if this expression can be used
+ * for static initialisation, otherwise -1.
+ *
+ * Constant initialisation expressions must be costant or an address
+ * of a static object with an optional offset. In the first case,
+ * the result is returned in *offsp. In the second case, the static
+ * object is returned in *symp and the offset in *offsp.
+ *
+ * The expression can consist of PLUS, MINUS, AMPER, NAME, STRING and
+ * CON. Type conversions are allowed if they do not change binary
+ * representation (including width).
+ */
+int
+conaddr(tn, symp, offsp)
+ tnode_t *tn;
+ sym_t **symp;
+ ptrdiff_t *offsp;
+{
+ sym_t *sym;
+ ptrdiff_t offs1, offs2;
+ tspec_t t, ot;
+
+ switch (tn->tn_op) {
+ case MINUS:
+ if (tn->tn_right->tn_op != CON)
+ return (-1);
+ /* FALLTHROUGH */
+ case PLUS:
+ offs1 = offs2 = 0;
+ if (tn->tn_left->tn_op == CON) {
+ offs1 = (ptrdiff_t)tn->tn_left->tn_val->v_quad;
+ if (conaddr(tn->tn_right, &sym, &offs2) == -1)
+ return (-1);
+ } else if (tn->tn_right->tn_op == CON) {
+ offs2 = (ptrdiff_t)tn->tn_right->tn_val->v_quad;
+ if (tn->tn_op == MINUS)
+ offs2 = -offs2;
+ if (conaddr(tn->tn_left, &sym, &offs1) == -1)
+ return (-1);
+ } else {
+ return (-1);
+ }
+ *symp = sym;
+ *offsp = offs1 + offs2;
+ break;
+ case AMPER:
+ if (tn->tn_left->tn_op == NAME) {
+ *symp = tn->tn_left->tn_sym;
+ *offsp = 0;
+ } else if (tn->tn_left->tn_op == STRING) {
+ /*
+ * If this would be the front end of a compiler we
+ * would return a label instead of 0.
+ */
+ *offsp = 0;
+ }
+ break;
+ case CVT:
+ t = tn->tn_type->t_tspec;
+ ot = tn->tn_left->tn_type->t_tspec;
+ if ((!isityp(t) && t != PTR) || (!isityp(ot) && ot != PTR)) {
+ return (-1);
+ } else if (psize(t) != psize(ot)) {
+ return (-1);
+ }
+ if (conaddr(tn->tn_left, symp, offsp) == -1)
+ return (-1);
+ break;
+ default:
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Concatenate two string constants.
+ */
+strg_t *
+catstrg(strg1, strg2)
+ strg_t *strg1, *strg2;
+{
+ size_t len1, len2, len;
+
+ if (strg1->st_tspec != strg2->st_tspec) {
+ /* cannot concatenate wide and regular string literals */
+ error(292);
+ return (strg1);
+ }
+
+ len = (len1 = strg1->st_len) + (len2 = strg2->st_len);
+
+ if (strg1->st_tspec == CHAR) {
+ strg1->st_cp = xrealloc(strg1->st_cp, len + 1);
+ (void)memcpy(strg1->st_cp + len1, strg2->st_cp, len2 + 1);
+ free(strg2->st_cp);
+ } else {
+ strg1->st_wcp = xrealloc(strg1->st_wcp,
+ (len + 1) * sizeof (wchar_t));
+ (void)memcpy(strg1->st_wcp + len1, strg2->st_wcp,
+ (len2 + 1) * sizeof (wchar_t));
+ free(strg2->st_wcp);
+ }
+ free(strg2);
+
+ return (strg1);
+}
+
+/*
+ * Print a warning if the given node has operands which should be
+ * parenthesized.
+ *
+ * XXX Does not work if an operand is a constant expression. Constant
+ * expressions are already folded.
+ */
+static void
+precconf(tn)
+ tnode_t *tn;
+{
+ tnode_t *ln, *rn;
+ op_t lop, rop;
+ int lparn, rparn;
+ mod_t *mp;
+ int warn;
+
+ if (!hflag)
+ return;
+
+ mp = &modtab[tn->tn_op];
+
+ lparn = 0;
+ for (ln = tn->tn_left; ln->tn_op == CVT; ln = ln->tn_left)
+ lparn |= ln->tn_parn;
+ lparn |= ln->tn_parn;
+ lop = ln->tn_op;
+
+ if (mp->m_binary) {
+ rparn = 0;
+ for (rn = tn->tn_right; tn->tn_op == CVT; rn = rn->tn_left)
+ rparn |= rn->tn_parn;
+ rparn |= rn->tn_parn;
+ rop = rn->tn_op;
+ }
+
+ warn = 0;
+
+ switch (tn->tn_op) {
+ case SHL:
+ case SHR:
+ if (!lparn && (lop == PLUS || lop == MINUS)) {
+ warn = 1;
+ } else if (!rparn && (rop == PLUS || rop == MINUS)) {
+ warn = 1;
+ }
+ break;
+ case LOGOR:
+ if (!lparn && lop == LOGAND) {
+ warn = 1;
+ } else if (!rparn && rop == LOGAND) {
+ warn = 1;
+ }
+ break;
+ case AND:
+ case XOR:
+ case OR:
+ if (!lparn && lop != tn->tn_op) {
+ if (lop == PLUS || lop == MINUS) {
+ warn = 1;
+ } else if (lop == AND || lop == XOR) {
+ warn = 1;
+ }
+ }
+ if (!warn && !rparn && rop != tn->tn_op) {
+ if (rop == PLUS || rop == MINUS) {
+ warn = 1;
+ } else if (rop == AND || rop == XOR) {
+ warn = 1;
+ }
+ }
+ break;
+ /* LINTED (enumeration values not handled in switch) */
+ default:
+ }
+
+ if (warn) {
+ /* precedence confusion possible: parenthesize! */
+ warning(169);
+ }
+
+}
diff --git a/usr.bin/xlint/lint2/Makefile b/usr.bin/xlint/lint2/Makefile
new file mode 100644
index 0000000..0e7d405
--- /dev/null
+++ b/usr.bin/xlint/lint2/Makefile
@@ -0,0 +1,13 @@
+# $NetBSD: Makefile,v 1.2 1995/07/03 21:24:39 cgd Exp $
+
+.PATH: ${.CURDIR}/../lint1
+
+PROG= lint2
+SRCS= main2.c hash.c read.c mem.c mem2.c chk.c msg.c emit.c emit2.c
+NOMAN=
+CFLAGS+=-I${.CURDIR}/../lint1
+LINTFLAGS=-abehrz
+
+BINDIR= /usr/libexec
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/xlint/lint2/chk.c b/usr.bin/xlint/lint2/chk.c
new file mode 100644
index 0000000..0aac852
--- /dev/null
+++ b/usr.bin/xlint/lint2/chk.c
@@ -0,0 +1,1462 @@
+/* $NetBSD: chk.c,v 1.2 1995/07/03 21:24:42 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: chk.c,v 1.2 1995/07/03 21:24:42 cgd Exp $";
+#endif
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <limits.h>
+#include <err.h>
+
+#include "lint2.h"
+
+/* various type information */
+ttab_t ttab[NTSPEC];
+
+
+static void chkund __P((hte_t *));
+static void chkdnu __P((hte_t *));
+static void chkdnud __P((hte_t *));
+static void chkmd __P((hte_t *));
+static void chkvtui __P((hte_t *, sym_t *, sym_t *));
+static void chkvtdi __P((hte_t *, sym_t *, sym_t *));
+static void chkfaui __P((hte_t *, sym_t *, sym_t *));
+static void chkau __P((hte_t *, int, sym_t *, sym_t *, pos_t *,
+ fcall_t *, fcall_t *, type_t *, type_t *));
+static void chkrvu __P((hte_t *, sym_t *));
+static void chkadecl __P((hte_t *, sym_t *, sym_t *));
+static void printflike __P((hte_t *,fcall_t *, int,
+ const char *, type_t **));
+static void scanflike __P((hte_t *, fcall_t *, int,
+ const char *, type_t **));
+static void badfmt __P((hte_t *, fcall_t *));
+static void inconarg __P((hte_t *, fcall_t *, int));
+static void tofewarg __P((hte_t *, fcall_t *));
+static void tomanyarg __P((hte_t *, fcall_t *));
+static int eqtype __P((type_t *, type_t *, int, int, int, int *));
+static int eqargs __P((type_t *, type_t *, int *));
+static int mnoarg __P((type_t *, int *));
+
+
+void
+inittyp()
+{
+ int i;
+ static struct {
+ tspec_t it_tspec;
+ ttab_t it_ttab;
+ } ittab[] = {
+ { SIGNED, { 0, 0,
+ SIGNED, UNSIGN,
+ 0, 0, 0, 0, 0, "signed" } },
+ { UNSIGN, { 0, 0,
+ SIGNED, UNSIGN,
+ 0, 0, 0, 0, 0, "unsigned" } },
+ { CHAR, { CHAR_BIT, CHAR_BIT,
+ SCHAR, UCHAR,
+ 1, 0, 0, 1, 1, "char" } },
+ { SCHAR, { CHAR_BIT, CHAR_BIT,
+ SCHAR, UCHAR,
+ 1, 0, 0, 1, 1, "signed char" } },
+ { UCHAR, { CHAR_BIT, CHAR_BIT,
+ SCHAR, UCHAR,
+ 1, 1, 0, 1, 1, "unsigned char" } },
+ { SHORT, { sizeof (short) * CHAR_BIT, 2 * CHAR_BIT,
+ SHORT, USHORT,
+ 1, 0, 0, 1, 1, "short" } },
+ { USHORT, { sizeof (u_short) * CHAR_BIT, 2 * CHAR_BIT,
+ SHORT, USHORT,
+ 1, 1, 0, 1, 1, "unsigned short" } },
+ { INT, { sizeof (int) * CHAR_BIT, 3 * CHAR_BIT,
+ INT, UINT,
+ 1, 0, 0, 1, 1, "int" } },
+ { UINT, { sizeof (u_int) * CHAR_BIT, 3 * CHAR_BIT,
+ INT, UINT,
+ 1, 1, 0, 1, 1, "unsigned int" } },
+ { LONG, { sizeof (long) * CHAR_BIT, 4 * CHAR_BIT,
+ LONG, ULONG,
+ 1, 0, 0, 1, 1, "long" } },
+ { ULONG, { sizeof (u_long) * CHAR_BIT, 4 * CHAR_BIT,
+ LONG, ULONG,
+ 1, 1, 0, 1, 1, "unsigned long" } },
+ { QUAD, { sizeof (quad_t) * CHAR_BIT, 8 * CHAR_BIT,
+ QUAD, UQUAD,
+ 1, 0, 0, 1, 1, "long long" } },
+ { UQUAD, { sizeof (u_quad_t) * CHAR_BIT, 8 * CHAR_BIT,
+ QUAD, UQUAD,
+ 1, 1, 0, 1, 1, "unsigned long long" } },
+ { FLOAT, { sizeof (float) * CHAR_BIT, 4 * CHAR_BIT,
+ FLOAT, FLOAT,
+ 0, 0, 1, 1, 1, "float" } },
+ { DOUBLE, { sizeof (double) * CHAR_BIT, 8 * CHAR_BIT,
+ DOUBLE, DOUBLE,
+ 0, 0, 1, 1, 1, "double" } },
+ { LDOUBLE, { sizeof (ldbl_t) * CHAR_BIT, 10 * CHAR_BIT,
+ LDOUBLE, LDOUBLE,
+ 0, 0, 1, 1, 1, "long double" } },
+ { VOID, { -1, -1,
+ VOID, VOID,
+ 0, 0, 0, 0, 0, "void" } },
+ { STRUCT, { -1, -1,
+ STRUCT, STRUCT,
+ 0, 0, 0, 0, 0, "struct" } },
+ { UNION, { -1, -1,
+ UNION, UNION,
+ 0, 0, 0, 0, 0, "union" } },
+ { ENUM, { sizeof (int) * CHAR_BIT, 3 * CHAR_BIT,
+ ENUM, ENUM,
+ 1, 0, 0, 1, 1, "enum" } },
+ { PTR, { sizeof (void *) * CHAR_BIT, 4 * CHAR_BIT,
+ PTR, PTR,
+ 0, 1, 0, 0, 1, "pointer" } },
+ { ARRAY, { -1, -1,
+ ARRAY, ARRAY,
+ 0, 0, 0, 0, 0, "array" } },
+ { FUNC, { -1, -1,
+ FUNC, FUNC,
+ 0, 0, 0, 0, 0, "function" } },
+ };
+
+ for (i = 0; i < sizeof (ittab) / sizeof (ittab[0]); i++)
+ STRUCT_ASSIGN(ttab[ittab[i].it_tspec], ittab[i].it_ttab);
+ if (!pflag) {
+ for (i = 0; i < NTSPEC; i++)
+ ttab[i].tt_psz = ttab[i].tt_sz;
+ }
+}
+
+
+/*
+ * If there is a symbol named "main", mark it as used.
+ */
+void
+mainused()
+{
+ hte_t *hte;
+
+ if ((hte = hsearch("main", 0)) != NULL)
+ hte->h_used = 1;
+}
+
+/*
+ * Performs all tests for a single name
+ */
+void
+chkname(hte)
+ hte_t *hte;
+{
+ sym_t *sym, *def, *pdecl, *decl;
+
+ if (uflag) {
+ chkund(hte);
+ chkdnu(hte);
+ if (xflag)
+ chkdnud(hte);
+ }
+ chkmd(hte);
+
+ /* Get definition, prototype declaration and declaration */
+ def = pdecl = decl = NULL;
+ for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) {
+ if (def == NULL && (sym->s_def == DEF || sym->s_def == TDEF))
+ def = sym;
+ if (pdecl == NULL && sym->s_def == DECL &&
+ TP(sym->s_type)->t_tspec == FUNC &&
+ TP(sym->s_type)->t_proto) {
+ pdecl = sym;
+ }
+ if (decl == NULL && sym->s_def == DECL)
+ decl = sym;
+ }
+
+ /* A prototype is better than an old style declaration. */
+ if (pdecl != NULL)
+ decl = pdecl;
+
+ chkvtui(hte, def, decl);
+
+ chkvtdi(hte, def, decl);
+
+ chkfaui(hte, def, decl);
+
+ chkrvu(hte, def);
+
+ chkadecl(hte, def, decl);
+}
+
+/*
+ * Print a warning if the name has been used, but not defined.
+ */
+static void
+chkund(hte)
+ hte_t *hte;
+{
+ fcall_t *fcall;
+ usym_t *usym;
+
+ if (!hte->h_used || hte->h_def)
+ return;
+
+ if ((fcall = hte->h_calls) != NULL) {
+ /* %s used( %s ), but not defined */
+ msg(0, hte->h_name, mkpos(&fcall->f_pos));
+ } else if ((usym = hte->h_usyms) != NULL) {
+ /* %s used( %s ), but not defined */
+ msg(0, hte->h_name, mkpos(&usym->u_pos));
+ }
+}
+
+/*
+ * Print a warning if the name has been defined, but never used.
+ */
+static void
+chkdnu(hte)
+ hte_t *hte;
+{
+ sym_t *sym;
+
+ if (!hte->h_def || hte->h_used)
+ return;
+
+ for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) {
+ if (sym->s_def == DEF || sym->s_def == TDEF) {
+ /* %s defined( %s ), but never used */
+ msg(1, hte->h_name, mkpos(&sym->s_pos));
+ break;
+ }
+ }
+}
+
+/*
+ * Print a warning if the name has been declared, but is not used
+ * or defined.
+ */
+static void
+chkdnud(hte)
+ hte_t *hte;
+{
+ sym_t *sym;
+
+ if (hte->h_syms == NULL || hte->h_used || hte->h_def)
+ return;
+
+ if ((sym = hte->h_syms) != NULL) {
+ if (sym->s_def != DECL)
+ errx(1, "internal error: chkdnud() 1");
+ /* %s declared( %s ), but never used or defined */
+ msg(2, hte->h_name, mkpos(&sym->s_pos));
+ }
+}
+
+/*
+ * Print a warning if there is more then one definition for
+ * this name.
+ */
+static void
+chkmd(hte)
+ hte_t *hte;
+{
+ sym_t *sym, *def1;
+ char *pos1;
+
+ if (!hte->h_def)
+ return;
+
+ def1 = NULL;
+ for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) {
+ /*
+ * ANSI C allows tentative definitions of the same name in
+ * only one compilation unit.
+ */
+ if (sym->s_def != DEF && (!sflag || sym->s_def != TDEF))
+ continue;
+ if (def1 == NULL) {
+ def1 = sym;
+ continue;
+ }
+ pos1 = xstrdup(mkpos(&def1->s_pos));
+ /* %s multiply defined\t%s :: %s */
+ msg(3, hte->h_name, pos1, mkpos(&sym->s_pos));
+ free(pos1);
+ }
+}
+
+/*
+ * Print a warning if the return value assumed for a function call
+ * differs from the return value of the function definition or
+ * function declaration.
+ *
+ * If no definition/declaration can be found, the assumed return values
+ * are always int. So there is no need to compare with another function
+ * call as it's done for function arguments.
+ */
+static void
+chkvtui(hte, def, decl)
+ hte_t *hte;
+ sym_t *def, *decl;
+{
+ fcall_t *call;
+ char *pos1;
+ type_t *tp1, *tp2;
+ /* LINTED (automatic hides external declaration: warn) */
+ int warn, eq;
+ tspec_t t1;
+
+ if (hte->h_calls == NULL)
+ return;
+
+ if (def == NULL)
+ def = decl;
+ if (def == NULL)
+ return;
+
+ t1 = (tp1 = TP(def->s_type)->t_subt)->t_tspec;
+ for (call = hte->h_calls; call != NULL; call = call->f_nxt) {
+ tp2 = TP(call->f_type)->t_subt;
+ eq = eqtype(tp1, tp2, 1, 0, 0, (warn = 0, &warn));
+ if (!call->f_rused) {
+ /* no return value used */
+ if ((t1 == STRUCT || t1 == UNION) && !eq) {
+ /*
+ * If a function returns a struct or union it
+ * must be declared to return a struct or
+ * union, also if the return value is ignored.
+ * This is necessary because the caller must
+ * allocate stack space for the return value.
+ * If it does not, the return value would over-
+ * write other data.
+ * XXX Following massage may be confusing
+ * because it appears also if the return value
+ * was declared inconsistently. But this
+ * behaviour matches pcc based lint, so it is
+ * accepted for now.
+ */
+ pos1 = xstrdup(mkpos(&def->s_pos));
+ /* %s value must be decl. before use %s :: %s */
+ msg(17, hte->h_name,
+ pos1, mkpos(&call->f_pos));
+ free(pos1);
+ }
+ continue;
+ }
+ if (!eq || (sflag && warn)) {
+ pos1 = xstrdup(mkpos(&def->s_pos));
+ /* %s value used inconsistenty\t%s :: %s */
+ msg(4, hte->h_name, pos1, mkpos(&call->f_pos));
+ free(pos1);
+ }
+ }
+}
+
+/*
+ * Print a warning if a definition/declaration does not match another
+ * definition/declaration of the same name. For functions, only the
+ * types of return values are tested.
+ */
+static void
+chkvtdi(hte, def, decl)
+ hte_t *hte;
+ sym_t *def, *decl;
+{
+ sym_t *sym;
+ type_t *tp1, *tp2;
+ /* LINTED (automatic hides external declaration: warn) */
+ int eq, warn;
+ char *pos1;
+
+ if (def == NULL)
+ def = decl;
+ if (def == NULL)
+ return;
+
+ tp1 = TP(def->s_type);
+ for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) {
+ if (sym == def)
+ continue;
+ tp2 = TP(sym->s_type);
+ warn = 0;
+ if (tp1->t_tspec == FUNC && tp2->t_tspec == FUNC) {
+ eq = eqtype(tp1->t_subt, tp2->t_subt, 1, 0, 0, &warn);
+ } else {
+ eq = eqtype(tp1, tp2, 0, 0, 0, &warn);
+ }
+ if (!eq || (sflag && warn)) {
+ pos1 = xstrdup(mkpos(&def->s_pos));
+ /* %s value declared inconsistently\t%s :: %s */
+ msg(5, hte->h_name, pos1, mkpos(&sym->s_pos));
+ free(pos1);
+ }
+ }
+}
+
+/*
+ * Print a warning if a function is called with arguments which does
+ * not match the function definition, declaration or another call
+ * of the same function.
+ */
+static void
+chkfaui(hte, def, decl)
+ hte_t *hte;
+ sym_t *def, *decl;
+{
+ type_t *tp1, *tp2, **ap1, **ap2;
+ pos_t *pos1p;
+ fcall_t *calls, *call, *call1;
+ int n, as;
+ char *pos1;
+ arginf_t *ai;
+
+ if ((calls = hte->h_calls) == NULL)
+ return;
+
+ /*
+ * If we find a function definition, we use this for comparision,
+ * otherwise the first prototype we can find. If there is no
+ * definition or prototype declaration, the first function call
+ * is used.
+ */
+ tp1 = NULL;
+ call1 = NULL;
+ if (def != NULL) {
+ if ((tp1 = TP(def->s_type))->t_tspec != FUNC)
+ return;
+ pos1p = &def->s_pos;
+ } else if (decl != NULL && TP(decl->s_type)->t_proto) {
+ if ((tp1 = TP(decl->s_type))->t_tspec != FUNC)
+ return;
+ pos1p = &decl->s_pos;
+ }
+ if (tp1 == NULL) {
+ call1 = calls;
+ calls = calls->f_nxt;
+ if ((tp1 = TP(call1->f_type))->t_tspec != FUNC)
+ return;
+ pos1p = &call1->f_pos;
+ }
+
+ n = 1;
+ for (call = calls; call != NULL; call = call->f_nxt) {
+ if ((tp2 = TP(call->f_type))->t_tspec != FUNC)
+ continue;
+ ap1 = tp1->t_args;
+ ap2 = tp2->t_args;
+ n = 0;
+ while (*ap1 != NULL && *ap2 != NULL) {
+ if (def != NULL && def->s_va && n >= def->s_nva)
+ break;
+ n++;
+ chkau(hte, n, def, decl, pos1p, call1, call,
+ *ap1, *ap2);
+ ap1++;
+ ap2++;
+ }
+ if (*ap1 == *ap2) {
+ /* equal # of arguments */
+ } else if (def != NULL && def->s_va && n >= def->s_nva) {
+ /*
+ * function definition with VARARGS; The # of
+ * arguments of the call must be at least as large
+ * as the parameter of VARARGS.
+ */
+ } else if (*ap2 != NULL && tp1->t_proto && tp1->t_vararg) {
+ /*
+ * prototype with ... and function call with
+ * at least the same # of arguments as declared
+ * in the prototype.
+ */
+ } else {
+ pos1 = xstrdup(mkpos(pos1p));
+ /* %s: variable # of args\t%s :: %s */
+ msg(7, hte->h_name, pos1, mkpos(&call->f_pos));
+ free(pos1);
+ continue;
+ }
+
+ /* perform SCANFLIKE/PRINTFLIKE tests */
+ if (def == NULL || (!def->s_prfl && !def->s_scfl))
+ continue;
+ as = def->s_prfl ? def->s_nprfl : def->s_nscfl;
+ for (ai = call->f_args; ai != NULL; ai = ai->a_nxt) {
+ if (ai->a_num == as)
+ break;
+ }
+ if (ai == NULL || !ai->a_fmt)
+ continue;
+ if (def->s_prfl) {
+ printflike(hte, call, n, ai->a_fstrg, ap2);
+ } else {
+ scanflike(hte, call, n, ai->a_fstrg, ap2);
+ }
+ }
+}
+
+/*
+ * Check a single argument in a function call.
+ *
+ * hte a pointer to the hash table entry of the function
+ * n the number of the argument (1..)
+ * def the function definition or NULL
+ * decl prototype declaration, old style declaration or NULL
+ * pos1p position of definition, declaration of first call
+ * call1 first call, if both def and decl are old style def/decl
+ * call checked call
+ * arg1 currently checked argument of def/decl/call1
+ * arg2 currently checked argument of call
+ *
+ */
+static void
+chkau(hte, n, def, decl, pos1p, call1, call, arg1, arg2)
+ hte_t *hte;
+ int n;
+ sym_t *def, *decl;
+ pos_t *pos1p;
+ fcall_t *call1, *call;
+ type_t *arg1, *arg2;
+{
+ /* LINTED (automatic hides external declaration: warn) */
+ int promote, asgn, warn;
+ tspec_t t1, t2;
+ arginf_t *ai, *ai1;
+ char *pos1;
+
+ /*
+ * If a function definition is available (def != NULL), we compair the
+ * function call (call) with the definition. Otherwise, if a function
+ * definition is available and it is not an old style definition
+ * (decl != NULL && TP(decl->s_type)->t_proto), we compair the call
+ * with this declaration. Otherwise we compair it with the first
+ * call we have found (call1).
+ */
+
+ /* arg1 must be promoted if it stems from an old style definition */
+ promote = def != NULL && def->s_osdef;
+
+ /*
+ * If we compair with a definition or declaration, we must perform
+ * the same checks for qualifiers in indirected types as in
+ * assignments.
+ */
+ asgn = def != NULL || (decl != NULL && TP(decl->s_type)->t_proto);
+
+ warn = 0;
+ if (eqtype(arg1, arg2, 1, promote, asgn, &warn) && (!sflag || !warn))
+ return;
+
+ /*
+ * Other lint implementations print warnings as soon as the type
+ * of an argument does not match exactly the expected type. The
+ * result are lots of warnings which are really not neccessary.
+ * We print a warning only if
+ * (0) at least one type is not an interger type and types differ
+ * (1) hflag is set and types differ
+ * (2) types differ, except in signedness
+ * If the argument is an integer constant whose msb is not set,
+ * signedness is ignored (e.g. 0 matches both signed and unsigned
+ * int). This is with and without hflag.
+ * If the argument is an integer constant with value 0 and the
+ * expected argument is of type pointer and the width of the
+ * interger constant is the same as the width of the pointer,
+ * no warning is printed.
+ */
+ t1 = arg1->t_tspec;
+ t2 = arg2->t_tspec;
+ if (isityp(t1) && isityp(t2) && !arg1->t_isenum && !arg2->t_isenum) {
+ if (promote) {
+ /*
+ * XXX Here is a problem: Althrough it is possible to
+ * pass an int where a char/short it expected, there
+ * may be loss in significant digits. We should first
+ * check for const arguments if they can be converted
+ * into the original parameter type.
+ */
+ if (t1 == FLOAT) {
+ t1 = DOUBLE;
+ } else if (t1 == CHAR || t1 == SCHAR) {
+ t1 = INT;
+ } else if (t1 == UCHAR) {
+ t1 = tflag ? UINT : INT;
+ } else if (t1 == SHORT) {
+ t1 = INT;
+ } else if (t1 == USHORT) {
+ /* CONSTCOND */
+ t1 = INT_MAX < USHRT_MAX || tflag ? UINT : INT;
+ }
+ }
+
+ if (styp(t1) == styp(t2)) {
+
+ /*
+ * types differ only in signedness; get information
+ * about arguments
+ */
+
+ /*
+ * treat a definition like a call with variable
+ * arguments
+ */
+ ai1 = call1 != NULL ? call1->f_args : NULL;
+
+ /*
+ * if two calls are compared, ai1 is set to the
+ * information for the n-th argument, if this was
+ * a constant, otherwise to NULL
+ */
+ for ( ; ai1 != NULL; ai1 = ai1->a_nxt) {
+ if (ai1->a_num == n)
+ break;
+ }
+ /*
+ * ai is set to the information of the n-th arg
+ * of the (second) call, if this was a constant,
+ * otherwise to NULL
+ */
+ for (ai = call->f_args; ai != NULL; ai = ai->a_nxt) {
+ if (ai->a_num == n)
+ break;
+ }
+
+ if (ai1 == NULL && ai == NULL) {
+ /* no constant at all */
+ if (!hflag)
+ return;
+ } else if (ai1 == NULL || ai == NULL) {
+ /* one constant */
+ if (ai == NULL)
+ ai = ai1;
+ if (ai->a_zero || ai->a_pcon)
+ /* same value in signed and unsigned */
+ return;
+ /* value (not representation) differently */
+ } else {
+ /*
+ * two constants, one signed, one unsigned;
+ * if the msb of one of the constants is set,
+ * the argument is used inconsistently.
+ */
+ if (!ai1->a_ncon && !ai->a_ncon)
+ return;
+ }
+ }
+
+ } else if (t1 == PTR && isityp(t2) && psize(t1) == psize(t2)) {
+ for (ai = call->f_args; ai != NULL; ai = ai->a_nxt) {
+ if (ai->a_num == n)
+ break;
+ }
+ if (ai != NULL && ai->a_zero)
+ return;
+ }
+
+ pos1 = xstrdup(mkpos(pos1p));
+ /* %s, arg %d used inconsistently\t%s :: %s */
+ msg(6, hte->h_name, n, pos1, mkpos(&call->f_pos));
+ free(pos1);
+}
+
+/*
+ * Compare the types in the NULL-terminated array ap with the format
+ * string fmt.
+ */
+static void
+printflike(hte, call, n, fmt, ap)
+ hte_t *hte;
+ fcall_t *call;
+ int n;
+ const char *fmt;
+ type_t **ap;
+{
+ const char *fp;
+ int fc;
+ int fwidth, prec, left, sign, space, alt, zero;
+ tspec_t sz, t1, t2;
+ type_t *tp;
+
+ fp = fmt;
+ fc = *fp++;
+
+ for ( ; ; ) {
+ if (fc == '\0') {
+ if (*ap != NULL)
+ tomanyarg(hte, call);
+ break;
+ }
+ if (fc != '%') {
+ badfmt(hte, call);
+ break;
+ }
+ fc = *fp++;
+ fwidth = prec = left = sign = space = alt = zero = 0;
+ sz = NOTSPEC;
+
+ /* Flags */
+ for ( ; ; ) {
+ if (fc == '-') {
+ if (left)
+ break;
+ left = 1;
+ } else if (fc == '+') {
+ if (sign)
+ break;
+ sign = 1;
+ } else if (fc == ' ') {
+ if (space)
+ break;
+ space = 1;
+ } else if (fc == '#') {
+ if (alt)
+ break;
+ alt = 1;
+ } else if (fc == '0') {
+ if (zero)
+ break;
+ zero = 1;
+ } else {
+ break;
+ }
+ fc = *fp++;
+ }
+
+ /* field width */
+ if (isdigit(fc)) {
+ fwidth = 1;
+ do { fc = *fp++; } while (isdigit(fc)) ;
+ } else if (fc == '*') {
+ fwidth = 1;
+ fc = *fp++;
+ if ((tp = *ap++) == NULL) {
+ tofewarg(hte, call);
+ break;
+ }
+ n++;
+ if ((t1 = tp->t_tspec) != INT && (hflag || t1 != UINT))
+ inconarg(hte, call, n);
+ }
+
+ /* precision */
+ if (fc == '.') {
+ fc = *fp++;
+ prec = 1;
+ if (isdigit(fc)) {
+ do { fc = *fp++; } while (isdigit(fc));
+ } else if (fc == '*') {
+ fc = *fp++;
+ if ((tp = *ap++) == NULL) {
+ tofewarg(hte, call);
+ break;
+ }
+ n++;
+ if (tp->t_tspec != INT)
+ inconarg(hte, call, n);
+ } else {
+ badfmt(hte, call);
+ break;
+ }
+ }
+
+ if (fc == 'h') {
+ sz = SHORT;
+ } else if (fc == 'l') {
+ sz = LONG;
+ } else if (fc == 'q') {
+ sz = QUAD;
+ } else if (fc == 'L') {
+ sz = LDOUBLE;
+ }
+ if (sz != NOTSPEC)
+ fc = *fp++;
+
+ if (fc == '%') {
+ if (sz != NOTSPEC || left || sign || space ||
+ alt || zero || prec || fwidth) {
+ badfmt(hte, call);
+ }
+ fc = *fp++;
+ continue;
+ }
+
+ if (fc == '\0') {
+ badfmt(hte, call);
+ break;
+ }
+
+ if ((tp = *ap++) == NULL) {
+ tofewarg(hte, call);
+ break;
+ }
+ n++;
+ if ((t1 = tp->t_tspec) == PTR)
+ t2 = tp->t_subt->t_tspec;
+
+ if (fc == 'd' || fc == 'i') {
+ if (alt || sz == LDOUBLE) {
+ badfmt(hte, call);
+ break;
+ }
+ int_conv:
+ if (sz == LONG) {
+ if (t1 != LONG && (hflag || t1 != ULONG))
+ inconarg(hte, call, n);
+ } else if (sz == QUAD) {
+ if (t1 != QUAD && (hflag || t1 != UQUAD))
+ inconarg(hte, call, n);
+ } else {
+ /*
+ * SHORT is always promoted to INT, USHORT
+ * to INT or UINT.
+ */
+ if (t1 != INT && (hflag || t1 != UINT))
+ inconarg(hte, call, n);
+ }
+ } else if (fc == 'o' || fc == 'u' || fc == 'x' || fc == 'X') {
+ if ((alt && fc == 'u') || sz == LDOUBLE)
+ badfmt(hte, call);
+ uint_conv:
+ if (sz == LONG) {
+ if (t1 != ULONG && (hflag || t1 != LONG))
+ inconarg(hte, call, n);
+ } else if (sz == QUAD) {
+ if (t1 != UQUAD && (hflag || t1 != QUAD))
+ inconarg(hte, call, n);
+ } else if (sz == SHORT) {
+ /* USHORT was promoted to INT or UINT */
+ if (t1 != UINT && t1 != INT)
+ inconarg(hte, call, n);
+ } else {
+ if (t1 != UINT && (hflag || t1 != INT))
+ inconarg(hte, call, n);
+ }
+ } else if (fc == 'D' || fc == 'O' || fc == 'U') {
+ if ((alt && fc != 'O') || sz != NOTSPEC || !tflag)
+ badfmt(hte, call);
+ sz = LONG;
+ if (fc == 'D') {
+ goto int_conv;
+ } else {
+ goto uint_conv;
+ }
+ } else if (fc == 'f' || fc == 'e' || fc == 'E' ||
+ fc == 'g' || fc == 'G') {
+ if (sz == NOTSPEC)
+ sz = DOUBLE;
+ if (sz != DOUBLE && sz != LDOUBLE)
+ badfmt(hte, call);
+ if (t1 != sz)
+ inconarg(hte, call, n);
+ } else if (fc == 'c') {
+ if (sz != NOTSPEC || alt || zero)
+ badfmt(hte, call);
+ if (t1 != INT)
+ inconarg(hte, call, n);
+ } else if (fc == 's') {
+ if (sz != NOTSPEC || alt || zero)
+ badfmt(hte, call);
+ if (t1 != PTR ||
+ (t2 != CHAR && t2 != UCHAR && t2 != SCHAR)) {
+ inconarg(hte, call, n);
+ }
+ } else if (fc == 'p') {
+ if (fwidth || prec || sz != NOTSPEC || alt || zero)
+ badfmt(hte, call);
+ if (t1 != PTR || (hflag && t2 != VOID))
+ inconarg(hte, call, n);
+ } else if (fc == 'n') {
+ if (fwidth || prec || alt || zero || sz == LDOUBLE)
+ badfmt(hte, call);
+ if (t1 != PTR) {
+ inconarg(hte, call, n);
+ } else if (sz == LONG) {
+ if (t2 != LONG && t2 != ULONG)
+ inconarg(hte, call, n);
+ } else if (sz == SHORT) {
+ if (t2 != SHORT && t2 != USHORT)
+ inconarg(hte, call, n);
+ } else {
+ if (t2 != INT && t2 != UINT)
+ inconarg(hte, call, n);
+ }
+ } else {
+ badfmt(hte, call);
+ break;
+ }
+
+ fc = *fp++;
+ }
+}
+
+/*
+ * Compare the types in the NULL-terminated array ap with the format
+ * string fmt.
+ */
+static void
+scanflike(hte, call, n, fmt, ap)
+ hte_t *hte;
+ fcall_t *call;
+ int n;
+ const char *fmt;
+ type_t **ap;
+{
+ const char *fp;
+ int fc;
+ int noasgn, fwidth;
+ tspec_t sz, t1, t2;
+ type_t *tp;
+
+ fp = fmt;
+ fc = *fp++;
+
+ for ( ; ; ) {
+ if (fc == '\0') {
+ if (*ap != NULL)
+ tomanyarg(hte, call);
+ break;
+ }
+ if (fc != '%') {
+ badfmt(hte, call);
+ break;
+ }
+ fc = *fp++;
+
+ noasgn = fwidth = 0;
+ sz = NOTSPEC;
+
+ if (fc == '*') {
+ noasgn = 1;
+ fc = *fp++;
+ }
+
+ if (isdigit(fc)) {
+ fwidth = 1;
+ do { fc = *fp++; } while (isdigit(fc));
+ }
+
+ if (fc == 'h') {
+ sz = SHORT;
+ } else if (fc == 'l') {
+ sz = LONG;
+ } else if (fc == 'q') {
+ sz = QUAD;
+ } else if (fc == 'L') {
+ sz = LDOUBLE;
+ }
+ if (sz != NOTSPEC)
+ fc = *fp++;
+
+ if (fc == '%') {
+ if (sz != NOTSPEC || noasgn || fwidth)
+ badfmt(hte, call);
+ fc = *fp++;
+ continue;
+ }
+
+ if (!noasgn) {
+ if ((tp = *ap++) == NULL) {
+ tofewarg(hte, call);
+ break;
+ }
+ n++;
+ if ((t1 = tp->t_tspec) == PTR)
+ t2 = tp->t_subt->t_tspec;
+ }
+
+ if (fc == 'd' || fc == 'i' || fc == 'n') {
+ if (sz == LDOUBLE)
+ badfmt(hte, call);
+ if (sz != SHORT && sz != LONG && sz != QUAD)
+ sz = INT;
+ conv:
+ if (!noasgn) {
+ if (t1 != PTR) {
+ inconarg(hte, call, n);
+ } else if (t2 != styp(sz)) {
+ inconarg(hte, call, n);
+ } else if (hflag && t2 != sz) {
+ inconarg(hte, call, n);
+ } else if (tp->t_subt->t_const) {
+ inconarg(hte, call, n);
+ }
+ }
+ } else if (fc == 'o' || fc == 'u' || fc == 'x') {
+ if (sz == LDOUBLE)
+ badfmt(hte, call);
+ if (sz == SHORT) {
+ sz = USHORT;
+ } else if (sz == LONG) {
+ sz = ULONG;
+ } else if (sz == QUAD) {
+ sz = UQUAD;
+ } else {
+ sz = UINT;
+ }
+ goto conv;
+ } else if (fc == 'D') {
+ if (sz != NOTSPEC || !tflag)
+ badfmt(hte, call);
+ sz = LONG;
+ goto conv;
+ } else if (fc == 'O') {
+ if (sz != NOTSPEC || !tflag)
+ badfmt(hte, call);
+ sz = ULONG;
+ goto conv;
+ } else if (fc == 'X') {
+ /*
+ * XXX valid in ANSI C, but in NetBSD's libc imple-
+ * mented as "lx". Thats why it should be avoided.
+ */
+ if (sz != NOTSPEC || !tflag)
+ badfmt(hte, call);
+ sz = ULONG;
+ goto conv;
+ } else if (fc == 'E') {
+ /*
+ * XXX valid in ANSI C, but in NetBSD's libc imple-
+ * mented as "lf". Thats why it should be avoided.
+ */
+ if (sz != NOTSPEC || !tflag)
+ badfmt(hte, call);
+ sz = DOUBLE;
+ goto conv;
+ } else if (fc == 'F') {
+ /* XXX only for backward compatibility */
+ if (sz != NOTSPEC || !tflag)
+ badfmt(hte, call);
+ sz = DOUBLE;
+ goto conv;
+ } else if (fc == 'G') {
+ /*
+ * XXX valid in ANSI C, but in NetBSD's libc not
+ * implemented
+ */
+ if (sz != NOTSPEC && sz != LONG && sz != LDOUBLE)
+ badfmt(hte, call);
+ goto fconv;
+ } else if (fc == 'e' || fc == 'f' || fc == 'g') {
+ fconv:
+ if (sz == NOTSPEC) {
+ sz = FLOAT;
+ } else if (sz == LONG) {
+ sz = DOUBLE;
+ } else if (sz != LDOUBLE) {
+ badfmt(hte, call);
+ sz = FLOAT;
+ }
+ goto conv;
+ } else if (fc == 's' || fc == '[' || fc == 'c') {
+ if (sz != NOTSPEC)
+ badfmt(hte, call);
+ if (fc == '[') {
+ if ((fc = *fp++) == '-') {
+ badfmt(hte, call);
+ fc = *fp++;
+ }
+ if (fc != ']') {
+ badfmt(hte, call);
+ if (fc == '\0')
+ break;
+ }
+ }
+ if (!noasgn) {
+ if (t1 != PTR) {
+ inconarg(hte, call, n);
+ } else if (t2 != CHAR && t2 != UCHAR &&
+ t2 != SCHAR) {
+ inconarg(hte, call, n);
+ }
+ }
+ } else if (fc == 'p') {
+ if (sz != NOTSPEC)
+ badfmt(hte, call);
+ if (!noasgn) {
+ if (t1 != PTR || t2 != PTR) {
+ inconarg(hte, call, n);
+ } else if (tp->t_subt->t_subt->t_tspec!=VOID) {
+ if (hflag)
+ inconarg(hte, call, n);
+ }
+ }
+ } else {
+ badfmt(hte, call);
+ break;
+ }
+
+ fc = *fp++;
+ }
+}
+
+static void
+badfmt(hte, call)
+ hte_t *hte;
+ fcall_t *call;
+{
+ /* %s: malformed format string\t%s */
+ msg(13, hte->h_name, mkpos(&call->f_pos));
+}
+
+static void
+inconarg(hte, call, n)
+ hte_t *hte;
+ fcall_t *call;
+ int n;
+{
+ /* %s, arg %d inconsistent with format\t%s(%d) */
+ msg(14, hte->h_name, n, mkpos(&call->f_pos));
+}
+
+static void
+tofewarg(hte, call)
+ hte_t *hte;
+ fcall_t *call;
+{
+ /* %s: too few args for format \t%s */
+ msg(15, hte->h_name, mkpos(&call->f_pos));
+}
+
+static void
+tomanyarg(hte, call)
+ hte_t *hte;
+ fcall_t *call;
+{
+ /* %s: too many args for format \t%s */
+ msg(16, hte->h_name, mkpos(&call->f_pos));
+}
+
+
+/*
+ * Print warnings for return values which are used, but not returned,
+ * or return values which are always or sometimes ignored.
+ */
+static void
+chkrvu(hte, def)
+ hte_t *hte;
+ sym_t *def;
+{
+ fcall_t *call;
+ int used, ignored;
+
+ if (def == NULL)
+ /* don't know wheter or not the functions returns a value */
+ return;
+
+ if (hte->h_calls == NULL)
+ return;
+
+ if (def->s_rval) {
+ /* function has return value */
+ used = ignored = 0;
+ for (call = hte->h_calls; call != NULL; call = call->f_nxt) {
+ used |= call->f_rused;
+ ignored |= !call->f_rused && !call->f_rdisc;
+ }
+ /*
+ * XXX as soon as we are able to disable single warnings
+ * the following dependencies from hflag should be removed.
+ * but for now I do'nt want to be botherd by this warnings
+ * which are almost always useless.
+ */
+ if (!used && ignored) {
+ if (hflag)
+ /* %s returns value which is always ignored */
+ msg(8, hte->h_name);
+ } else if (used && ignored) {
+ if (hflag)
+ /* %s returns value which is sometimes ign. */
+ msg(9, hte->h_name);
+ }
+ } else {
+ /* function has no return value */
+ for (call = hte->h_calls; call != NULL; call = call->f_nxt) {
+ if (call->f_rused)
+ /* %s value is used( %s ), but none ret. */
+ msg(10, hte->h_name, mkpos(&call->f_pos));
+ }
+ }
+}
+
+/*
+ * Print warnings for inconsistent argument declarations.
+ */
+static void
+chkadecl(hte, def, decl)
+ hte_t *hte;
+ sym_t *def, *decl;
+{
+ /* LINTED (automatic hides external declaration: warn) */
+ int osdef, eq, warn, n;
+ sym_t *sym1, *sym;
+ type_t **ap1, **ap2, *tp1, *tp2;
+ char *pos1;
+ const char *pos2;
+
+ osdef = 0;
+ if (def != NULL) {
+ osdef = def->s_osdef;
+ sym1 = def;
+ } else if (decl != NULL && TP(decl->s_type)->t_proto) {
+ sym1 = decl;
+ } else {
+ return;
+ }
+ if (TP(sym1->s_type)->t_tspec != FUNC)
+ return;
+
+ /*
+ * XXX Prototypes should also be compared with old style function
+ * declarations.
+ */
+
+ for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) {
+ if (sym == sym1 || !TP(sym->s_type)->t_proto)
+ continue;
+ ap1 = TP(sym1->s_type)->t_args;
+ ap2 = TP(sym->s_type)->t_args;
+ n = 0;
+ while (*ap1 != NULL && *ap2 != NULL) {
+ warn = 0;
+ eq = eqtype(*ap1, *ap2, 1, osdef, 0, &warn);
+ if (!eq || warn) {
+ pos1 = xstrdup(mkpos(&sym1->s_pos));
+ pos2 = mkpos(&sym->s_pos);
+ /* %s, arg %d declared inconsistently ... */
+ msg(11, hte->h_name, n + 1, pos1, pos2);
+ free(pos1);
+ }
+ n++;
+ ap1++;
+ ap2++;
+ }
+ if (*ap1 == *ap2) {
+ tp1 = TP(sym1->s_type);
+ tp2 = TP(sym->s_type);
+ if (tp1->t_vararg == tp2->t_vararg)
+ continue;
+ if (tp2->t_vararg &&
+ sym1->s_va && sym1->s_nva == n && !sflag) {
+ continue;
+ }
+ }
+ /* %s: variable # of args declared\t%s :: %s */
+ pos1 = xstrdup(mkpos(&sym1->s_pos));
+ msg(12, hte->h_name, pos1, mkpos(&sym->s_pos));
+ free(pos1);
+ }
+}
+
+
+/*
+ * Check compatibility of two types. Returns 1 if types are compatible,
+ * otherwise 0.
+ *
+ * ignqual if set, ignore qualifiers of outhermost type; used for
+ * function arguments
+ * promote if set, promote left type before comparision; used for
+ * comparisions of arguments with parameters of old style
+ * definitions
+ * asgn left indirected type must have at least the same qualifiers
+ * like right indirected type (for assignments and function
+ * arguments)
+ * *warn set to 1 if an old style declaration was compared with
+ * an incompatible prototype declaration
+ */
+static int
+eqtype(tp1, tp2, ignqual, promot, asgn, warn)
+ type_t *tp1, *tp2;
+ int ignqual, promot, asgn, *warn;
+{
+ tspec_t t, to;
+ int indir;
+
+ to = NOTSPEC;
+ indir = 0;
+
+ while (tp1 != NULL && tp2 != NULL) {
+
+ t = tp1->t_tspec;
+ if (promot) {
+ if (t == FLOAT) {
+ t = DOUBLE;
+ } else if (t == CHAR || t == SCHAR) {
+ t = INT;
+ } else if (t == UCHAR) {
+ t = tflag ? UINT : INT;
+ } else if (t == SHORT) {
+ t = INT;
+ } else if (t == USHORT) {
+ /* CONSTCOND */
+ t = INT_MAX < USHRT_MAX || tflag ? UINT : INT;
+ }
+ }
+
+ if (asgn && to == PTR) {
+ if (indir == 1 && (t == VOID || tp2->t_tspec == VOID))
+ return (1);
+ }
+
+ if (t != tp2->t_tspec) {
+ /*
+ * Give pointer to types which differ only in
+ * signedness a chance if not sflag and not hflag.
+ */
+ if (sflag || hflag || to != PTR)
+ return (0);
+ if (styp(t) != styp(tp2->t_tspec))
+ return (0);
+ }
+
+ if (tp1->t_isenum && tp2->t_isenum) {
+ if (tp1->t_istag && tp2->t_istag) {
+ return (tp1->t_tag == tp2->t_tag);
+ } else if (tp1->t_istynam && tp2->t_istynam) {
+ return (tp1->t_tynam == tp2->t_tynam);
+ } else {
+ return (0);
+ }
+ }
+
+ /*
+ * XXX Handle combinations of enum and int if eflag is set.
+ * But note: enum and 0 should be allowed.
+ */
+
+ if (asgn && indir == 1) {
+ if (!tp1->t_const && tp2->t_const)
+ return (0);
+ if (!tp1->t_volatile && tp2->t_volatile)
+ return (0);
+ } else if (!ignqual && !tflag) {
+ if (tp1->t_const != tp2->t_const)
+ return (0);
+ if (tp1->t_const != tp2->t_const)
+ return (0);
+ }
+
+ if (t == STRUCT || t == UNION) {
+ if (tp1->t_istag && tp2->t_istag) {
+ return (tp1->t_tag == tp2->t_tag);
+ } else if (tp1->t_istynam && tp2->t_istynam) {
+ return (tp1->t_tynam == tp2->t_tynam);
+ } else {
+ return (0);
+ }
+ }
+
+ if (t == ARRAY && tp1->t_dim != tp2->t_dim) {
+ if (tp1->t_dim != 0 && tp2->t_dim != 0)
+ return (0);
+ }
+
+ if (t == FUNC) {
+ if (tp1->t_proto && tp2->t_proto) {
+ if (!eqargs(tp1, tp2, warn))
+ return (0);
+ } else if (tp1->t_proto) {
+ if (!mnoarg(tp1, warn))
+ return (0);
+ } else if (tp2->t_proto) {
+ if (!mnoarg(tp2, warn))
+ return (0);
+ }
+ }
+
+ tp1 = tp1->t_subt;
+ tp2 = tp2->t_subt;
+ ignqual = promot = 0;
+ to = t;
+ indir++;
+
+ }
+
+ return (tp1 == tp2);
+}
+
+/*
+ * Compares arguments of two prototypes
+ */
+static int
+eqargs(tp1, tp2, warn)
+ type_t *tp1, *tp2;
+ int *warn;
+{
+ type_t **a1, **a2;
+
+ if (tp1->t_vararg != tp2->t_vararg)
+ return (0);
+
+ a1 = tp1->t_args;
+ a2 = tp2->t_args;
+
+ while (*a1 != NULL && *a2 != NULL) {
+
+ if (eqtype(*a1, *a2, 1, 0, 0, warn) == 0)
+ return (0);
+
+ a1++;
+ a2++;
+
+ }
+
+ return (*a1 == *a2);
+}
+
+/*
+ * mnoarg() (matches functions with no argument type information)
+ * returns 1 if all parameters of a prototype are compatible with
+ * and old style function declaration.
+ * This is the case if following conditions are met:
+ * 1. the prototype must have a fixed number of parameters
+ * 2. no parameter is of type float
+ * 3. no parameter is converted to another type if integer promotion
+ * is applied on it
+ */
+static int
+mnoarg(tp, warn)
+ type_t *tp;
+ int *warn;
+{
+ type_t **arg;
+ tspec_t t;
+
+ if (tp->t_vararg && warn != NULL)
+ *warn = 1;
+ for (arg = tp->t_args; *arg != NULL; arg++) {
+ if ((t = (*arg)->t_tspec) == FLOAT)
+ return (0);
+ if (t == CHAR || t == SCHAR || t == UCHAR)
+ return (0);
+ if (t == SHORT || t == USHORT)
+ return (0);
+ }
+ return (1);
+}
+
diff --git a/usr.bin/xlint/lint2/emit2.c b/usr.bin/xlint/lint2/emit2.c
new file mode 100644
index 0000000..82527b5
--- /dev/null
+++ b/usr.bin/xlint/lint2/emit2.c
@@ -0,0 +1,236 @@
+/* $NetBSD: emit2.c,v 1.2 1995/07/03 21:24:44 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: emit2.c,v 1.2 1995/07/03 21:24:44 cgd Exp $";
+#endif
+
+#include <err.h>
+
+#include "lint2.h"
+
+static void outtype __P((type_t *));
+static void outdef __P((hte_t *, sym_t *));
+static void dumpname __P((hte_t *));
+
+/*
+ * Write type into the output buffer.
+ */
+static void
+outtype(tp)
+ type_t *tp;
+{
+ int t, s, na;
+ tspec_t ts;
+ type_t **ap;
+
+ while (tp != NULL) {
+ if ((ts = tp->t_tspec) == INT && tp->t_isenum)
+ ts = ENUM;
+ switch (ts) {
+ case CHAR: t = 'C'; s = '\0'; break;
+ case SCHAR: t = 'C'; s = 's'; break;
+ case UCHAR: t = 'C'; s = 'u'; break;
+ case SHORT: t = 'S'; s = '\0'; break;
+ case USHORT: t = 'S'; s = 'u'; break;
+ case INT: t = 'I'; s = '\0'; break;
+ case UINT: t = 'I'; s = 'u'; break;
+ case LONG: t = 'L'; s = '\0'; break;
+ case ULONG: t = 'L'; s = 'u'; break;
+ case QUAD: t = 'Q'; s = '\0'; break;
+ case UQUAD: t = 'Q'; s = 'u'; break;
+ case FLOAT: t = 'D'; s = 's'; break;
+ case DOUBLE: t = 'D'; s = '\0'; break;
+ case LDOUBLE: t = 'D'; s = 'l'; break;
+ case VOID: t = 'V'; s = '\0'; break;
+ case PTR: t = 'P'; s = '\0'; break;
+ case ARRAY: t = 'A'; s = '\0'; break;
+ case ENUM: t = 'T'; s = 'e'; break;
+ case STRUCT: t = 'T'; s = 's'; break;
+ case UNION: t = 'T'; s = 'u'; break;
+ case FUNC:
+ if (tp->t_args != NULL && !tp->t_proto) {
+ t = 'f';
+ } else {
+ t = 'F';
+ }
+ s = '\0';
+ break;
+ default:
+ errx(1, "internal error: outtype() 1");
+ }
+ if (tp->t_const)
+ outchar('c');
+ if (tp->t_volatile)
+ outchar('v');
+ if (s != '\0')
+ outchar(s);
+ outchar(t);
+ if (ts == ARRAY) {
+ outint(tp->t_dim);
+ } else if (ts == ENUM || ts == STRUCT || ts == UNION) {
+ if (tp->t_istag) {
+ outint(1);
+ outname(tp->t_tag->h_name);
+ } else if (tp->t_istynam) {
+ outint(2);
+ outname(tp->t_tynam->h_name);
+ } else {
+ outint(0);
+ }
+ } else if (ts == FUNC && tp->t_args != NULL) {
+ na = 0;
+ for (ap = tp->t_args; *ap != NULL; ap++)
+ na++;
+ if (tp->t_vararg)
+ na++;
+ outint(na);
+ for (ap = tp->t_args; *ap != NULL; ap++)
+ outtype(*ap);
+ if (tp->t_vararg)
+ outchar('E');
+ }
+ tp = tp->t_subt;
+ }
+}
+
+/*
+ * Write a definition.
+ */
+static void
+outdef(hte, sym)
+ hte_t *hte;
+ sym_t *sym;
+{
+ /* reset output buffer */
+ outclr();
+
+ /* line number in C source file */
+ outint(0);
+
+ /* this is a definition */
+ outchar('d');
+
+ /* index of file where symbol was defined and line number of def. */
+ outint(0);
+ outchar('.');
+ outint(0);
+
+ /* flags */
+ if (sym->s_va) {
+ outchar('v'); /* varargs */
+ outint(sym->s_nva);
+ }
+ if (sym->s_scfl) {
+ outchar('S'); /* scanflike */
+ outint(sym->s_nscfl);
+ }
+ if (sym->s_prfl) {
+ outchar('P'); /* printflike */
+ outint(sym->s_nprfl);
+ }
+ /* definition or tentative definition */
+ outchar(sym->s_def == DEF ? 'd' : 't');
+ if (TP(sym->s_type)->t_tspec == FUNC) {
+ if (sym->s_rval)
+ outchar('r'); /* fkt. has return value */
+ if (sym->s_osdef)
+ outchar('o'); /* old style definition */
+ }
+ outchar('u'); /* used (no warning if not used) */
+
+ /* name */
+ outname(hte->h_name);
+
+ /* type */
+ outtype(TP(sym->s_type));
+}
+
+/*
+ * Write the first definition of a name into the lint library.
+ */
+static void
+dumpname(hte)
+ hte_t *hte;
+{
+ sym_t *sym, *def;
+
+ /* static and undefined symbols are not written */
+ if (hte->h_static || !hte->h_def)
+ return;
+
+ /*
+ * If there is a definition, write it. Otherwise write a tentative
+ * definition. This is neccessary because more than one tentative
+ * definition is allowed (except with sflag).
+ */
+ def = NULL;
+ for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) {
+ if (sym->s_def == DEF) {
+ def = sym;
+ break;
+ }
+ if (sym->s_def == TDEF && def == NULL)
+ def = sym;
+ }
+ if (def == NULL)
+ errx(1, "internal error: dumpname() %s", hte->h_name);
+
+ outdef(hte, def);
+}
+
+/*
+ * Write a new lint library.
+ */
+void
+outlib(name)
+ const char *name;
+{
+ /* Open of output file and initialisation of the output buffer */
+ outopen(name);
+
+ /* write name of lint library */
+ outsrc(name);
+
+ /* name of lint lib has index 0 */
+ outclr();
+ outint(0);
+ outchar('s');
+ outstrg(name);
+
+ /* write all definitions with external linkage */
+ forall(dumpname);
+
+ /* close the output */
+ outclose();
+}
diff --git a/usr.bin/xlint/lint2/externs2.h b/usr.bin/xlint/lint2/externs2.h
new file mode 100644
index 0000000..2e65e53
--- /dev/null
+++ b/usr.bin/xlint/lint2/externs2.h
@@ -0,0 +1,87 @@
+/* $NetBSD: externs2.h,v 1.2 1995/07/03 21:24:46 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+/*
+ * main.c
+ */
+extern int xflag;
+extern int uflag;
+extern int Cflag;
+extern const char *libname;
+extern int pflag;
+extern int sflag;
+extern int tflag;
+extern int Hflag;
+extern int hflag;
+extern int Fflag;
+
+
+/*
+ * hash.c
+ */
+extern void inithash __P((void));
+extern hte_t *hsearch __P((const char *, int));
+extern void forall __P((void (*)(hte_t *)));
+
+/*
+ * read.c
+ */
+extern const char **fnames;
+extern type_t **tlst;
+
+extern void readfile __P((const char *));
+extern void mkstatic __P((hte_t *));
+
+/*
+ * mem2.c
+ */
+extern void initmem __P((void));
+extern void *xalloc __P((size_t));
+
+/*
+ * chk.c
+ */
+extern void inittyp __P((void));
+extern void mainused __P((void));
+extern void chkname __P((hte_t *));
+
+/*
+ * msg.c
+ */
+extern void msg __P((int, ...));
+extern const char *mkpos __P((pos_t *));
+
+/*
+ * emit2.c
+ */
+extern void outlib __P((const char *));
diff --git a/usr.bin/xlint/lint2/hash.c b/usr.bin/xlint/lint2/hash.c
new file mode 100644
index 0000000..7901802
--- /dev/null
+++ b/usr.bin/xlint/lint2/hash.c
@@ -0,0 +1,123 @@
+/* $NetBSD: hash.c,v 1.2 1995/07/03 21:24:47 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: hash.c,v 1.2 1995/07/03 21:24:47 cgd Exp $";
+#endif
+
+#include <stddef.h>
+#include <string.h>
+#include <limits.h>
+
+#include "lint2.h"
+
+/* pointer to hash table, initialized in inithash() */
+static hte_t **htab;
+
+static int hash __P((const char *));
+
+/*
+ * Initialize hash table.
+ */
+void
+inithash()
+{
+ htab = xcalloc(HSHSIZ2, sizeof (hte_t *));
+}
+
+/*
+ * Compute hash value from a string.
+ */
+static int
+hash(s)
+ const char *s;
+{
+ u_int v;
+ const u_char *us;
+
+ v = 0;
+ for (us = (const u_char *)s; *us != '\0'; us++) {
+ v = (v << sizeof (v)) + *us;
+ v ^= v >> (sizeof (v) * CHAR_BIT - sizeof (v));
+ }
+ return (v % HSHSIZ2);
+}
+
+/*
+ * Look for a hash table entry. If no hash table entry for the
+ * given name exists and mknew is set, create a new one.
+ */
+hte_t *
+hsearch(s, mknew)
+ const char *s;
+ int mknew;
+{
+ int h;
+ hte_t *hte;
+
+ h = hash(s);
+ for (hte = htab[h]; hte != NULL; hte = hte->h_link) {
+ if (strcmp(hte->h_name, s) == 0)
+ break;
+ }
+
+ if (hte != NULL || !mknew)
+ return (hte);
+
+ /* create a new hte */
+ hte = xalloc(sizeof (hte_t));
+ hte->h_name = xstrdup(s);
+ hte->h_lsym = &hte->h_syms;
+ hte->h_lcall = &hte->h_calls;
+ hte->h_lusym = &hte->h_usyms;
+ hte->h_link = htab[h];
+ htab[h] = hte;
+
+ return (hte);
+}
+
+/*
+ * Call function f for each name in the hash table.
+ */
+void
+forall(f)
+ void (*f) __P((hte_t *));
+{
+ int i;
+ hte_t *hte;
+
+ for (i = 0; i < HSHSIZ2; i++) {
+ for (hte = htab[i]; hte != NULL; hte = hte->h_link)
+ (*f)(hte);
+ }
+}
diff --git a/usr.bin/xlint/lint2/lint2.h b/usr.bin/xlint/lint2/lint2.h
new file mode 100644
index 0000000..0ade110
--- /dev/null
+++ b/usr.bin/xlint/lint2/lint2.h
@@ -0,0 +1,177 @@
+/* $NetBSD: lint2.h,v 1.2 1995/07/03 21:24:49 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#include "lint.h"
+
+/*
+ * Types are described by structures of type type_t.
+ */
+typedef struct type {
+ tspec_t t_tspec; /* type specifier */
+ u_int t_const : 1; /* constant */
+ u_int t_volatile : 1; /* volatile */
+ u_int t_vararg : 1; /* function has variable number of arguments */
+ u_int t_isenum : 1; /* enum type */
+ u_int t_proto : 1; /* this is a prototype */
+ u_int t_istag : 1; /* tag with _t_tag valid */
+ u_int t_istynam : 1; /* tag with _t_tynam valid */
+ union {
+ int _t_dim; /* if the type is an ARRAY than this
+ is the dimension of the array. */
+ struct hte *_t_tag; /* hash table entry of tag if
+ t_isenum, STRUCT or UNION */
+ struct hte *_t_tynam; /* hash table entry of typename if
+ t_isenum, STRUCT or UNION */
+ struct type **_t_args; /* list of argument types if this
+ is a prototype */
+ } t_u;
+ struct type *t_subt; /* indirected type (array element, pointed to
+ type, type of return value) */
+} type_t;
+
+#define t_dim t_u._t_dim
+#define t_tag t_u._t_tag
+#define t_tynam t_u._t_tynam
+#define t_args t_u._t_args
+
+/*
+ * argument information
+ *
+ * Such a structure is created for each argument of a function call
+ * which is an integer constant or a constant string.
+ */
+typedef struct arginf {
+ int a_num; /* # of argument (1..) */
+ u_int a_zero : 1; /* argument is 0 */
+ u_int a_pcon : 1; /* msb of argument is not set */
+ u_int a_ncon : 1; /* msb of argument is set */
+ u_int a_fmt : 1; /* a_fstrg points to format string */
+ char *a_fstrg; /* format string */
+ struct arginf *a_nxt; /* information for next const. argument */
+} arginf_t;
+
+/*
+ * Keeps information about position in source file.
+ */
+typedef struct {
+ u_short p_src; /* index of name of translation unit
+ (the name which was specified at the
+ command line) */
+ u_short p_line; /* line number in p_src */
+ u_short p_isrc; /* index of (included) file */
+ u_short p_iline; /* line number in p_iline */
+} pos_t;
+
+/*
+ * Used for definitions and declarations
+ *
+ * To save memory, variable sized structures are used. If
+ * all s_va, s_prfl and s_scfl are not set, the memory allocated
+ * for a symbol is only large enough to keep the first member of
+ * struct sym, s_s.
+ */
+typedef struct sym {
+ struct {
+ pos_t s_pos; /* pos of def./decl. */
+#ifndef lint
+ u_int s_def : 3; /* DECL, TDEF or DEF */
+#else
+ def_t s_def;
+#endif
+ u_int s_rval : 1; /* function has return value */
+ u_int s_osdef : 1; /* old style function definition */
+ u_int s_static : 1; /* symbol is static */
+ u_int s_va : 1; /* check only first s_nva arguments */
+ u_int s_prfl : 1; /* printflike */
+ u_int s_scfl : 1; /* scanflike */
+ u_short s_type; /* type */
+ struct sym *s_nxt; /* next symbol with same name */
+ } s_s;
+ short s_nva;
+ short s_nprfl;
+ short s_nscfl;
+} sym_t;
+
+#define s_pos s_s.s_pos
+#define s_rval s_s.s_rval
+#define s_osdef s_s.s_osdef
+#define s_static s_s.s_static
+#define s_def s_s.s_def
+#define s_va s_s.s_va
+#define s_prfl s_s.s_prfl
+#define s_scfl s_s.s_scfl
+#define s_type s_s.s_type
+#define s_nxt s_s.s_nxt
+
+/*
+ * Used to store informations about function calls.
+ */
+typedef struct fcall {
+ pos_t f_pos; /* position of call */
+ u_int f_rused : 1; /* return value used */
+ u_int f_rdisc : 1; /* return value discarded (casted to void) */
+ u_short f_type; /* types of expected return value and args */
+ arginf_t *f_args; /* information about constant arguments */
+ struct fcall *f_nxt; /* next call of same function */
+} fcall_t;
+
+/*
+ * Used to store information about usage of symbols other
+ * than for function calls.
+ */
+typedef struct usym {
+ pos_t u_pos; /* position */
+ struct usym *u_nxt; /* next usage */
+} usym_t;
+
+/*
+ * hash table entry
+ */
+typedef struct hte {
+ const char *h_name; /* name */
+ u_int h_used : 1; /* symbol is used */
+ u_int h_def : 1; /* symbol is defined */
+ u_int h_static : 1; /* static symbol */
+ sym_t *h_syms; /* declarations and definitions */
+ sym_t **h_lsym; /* points to s_nxt of last decl./def. */
+ fcall_t *h_calls; /* function calls */
+ fcall_t **h_lcall; /* points to f_nxt of last call */
+ usym_t *h_usyms; /* usage info */
+ usym_t **h_lusym; /* points to u_nxt of last usage info */
+ struct hte *h_link; /* next hte with same hash function */
+} hte_t;
+
+/* maps type indices into pointers to type structs */
+#define TP(idx) (tlst[idx])
+
+#include "externs2.h"
diff --git a/usr.bin/xlint/lint2/main2.c b/usr.bin/xlint/lint2/main2.c
new file mode 100644
index 0000000..171344a
--- /dev/null
+++ b/usr.bin/xlint/lint2/main2.c
@@ -0,0 +1,190 @@
+/* $NetBSD: main2.c,v 1.2 1995/07/03 21:24:53 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: main2.c,v 1.2 1995/07/03 21:24:53 cgd Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "lint2.h"
+
+/* warnings for symbols which are declared but not defined or used */
+int xflag;
+
+/*
+ * warnings for symbols which are used and not defined or defined
+ * and not used
+ */
+int uflag = 1;
+
+/* Create a lint library in the current directory with name libname. */
+int Cflag;
+const char *libname;
+
+int pflag;
+
+/*
+ * warnings for (tentative) definitions of the same name in more then
+ * one translation unit
+ */
+int sflag;
+
+int tflag;
+
+/*
+ * If a complaint stems from a included file, print the name of the included
+ * file instead of the name spezified at the command line followed by '?'
+ */
+int Hflag;
+
+int hflag;
+
+/* Print full path names, not only the last component */
+int Fflag;
+
+/*
+ * List of libraries (from -l flag). These libraries are read after all
+ * other input files has been read and, for Cflag, after the new lint library
+ * has been written.
+ */
+const char **libs;
+
+static void usage __P((void));
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c, i;
+ size_t len;
+ char *lname;
+
+ libs = xcalloc(1, sizeof (char *));
+
+ opterr = 0;
+ while ((c = getopt(argc, argv, "hpstxuC:HFl:")) != -1) {
+ switch (c) {
+ case 's':
+ sflag = 1;
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'u':
+ uflag = 0;
+ break;
+ case 'x':
+ xflag = 1;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'C':
+ len = strlen(optarg);
+ lname = xmalloc(len + 10);
+ (void)sprintf(lname, "llib-l%s.ln", optarg);
+ libname = lname;
+ Cflag = 1;
+ uflag = 0;
+ break;
+ case 'H':
+ Hflag = 1;
+ break;
+ case 'h':
+ hflag = 1;
+ break;
+ case 'F':
+ Fflag = 1;
+ break;
+ case 'l':
+ for (i = 0; libs[i] != NULL; i++) ;
+ libs = xrealloc(libs, (i + 2) * sizeof (char *));
+ libs[i] = xstrdup(optarg);
+ libs[i + 1] = NULL;
+ break;
+ case '?':
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+
+ initmem();
+
+ /* initialize hash table */
+ inithash();
+
+ inittyp();
+
+ for (i = 0; i < argc; i++)
+ readfile(argv[i]);
+
+ /* write the lint library */
+ if (Cflag) {
+ forall(mkstatic);
+ outlib(libname);
+ }
+
+ /* read additional libraries */
+ for (i = 0; libs[i] != NULL; i++)
+ readfile(libs[i]);
+
+ forall(mkstatic);
+
+ mainused();
+
+ /* perform all tests */
+ forall(chkname);
+
+ exit(0);
+ /* NOTREACHED */
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: lint2 -hpstxuHF -Clib -l lib ... src1 ...\n");
+ exit(1);
+}
+
diff --git a/usr.bin/xlint/lint2/mem2.c b/usr.bin/xlint/lint2/mem2.c
new file mode 100644
index 0000000..ed97c6f
--- /dev/null
+++ b/usr.bin/xlint/lint2/mem2.c
@@ -0,0 +1,97 @@
+/* $NetBSD: mem2.c,v 1.3 1995/10/02 17:27:11 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: mem2.c,v 1.3 1995/10/02 17:27:11 jpo Exp $";
+#endif
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <string.h>
+#include <err.h>
+
+#include "lint2.h"
+
+/* length of new allocated memory blocks */
+static size_t mblklen;
+
+/* offset of next free byte in mbuf */
+static size_t nxtfree;
+
+/* current buffer to server memory requests from */
+static void *mbuf;
+
+void
+initmem()
+{
+ int pgsz;
+
+ pgsz = getpagesize();
+ mblklen = ((MBLKSIZ + pgsz - 1) / pgsz) * pgsz;
+
+ nxtfree = mblklen;
+}
+
+/*
+ * Allocate memory in large chunks to avoid space and time overhead of
+ * malloc(). This is possible because memory allocated by xalloc()
+ * need never to be freed.
+ */
+void *
+xalloc(sz)
+ size_t sz;
+{
+ void *ptr;
+ int prot, flags;
+
+ sz = ALIGN(sz);
+ if (nxtfree + sz > mblklen) {
+ /* use mmap() instead of malloc() to avoid malloc overhead. */
+ prot = PROT_READ | PROT_WRITE;
+ flags = MAP_ANON | MAP_PRIVATE;
+ mbuf = mmap(NULL, mblklen, prot, flags, -1, (off_t)0);
+ if (mbuf == (void *)MAP_FAILED)
+ err(1, "can't map memory");
+ if (ALIGN((u_long)mbuf) != (u_long)mbuf)
+ errx(1, "mapped address is not aligned");
+ (void)memset(mbuf, 0, mblklen);
+ nxtfree = 0;
+ }
+
+ ptr = (char *)mbuf + nxtfree;
+ nxtfree += sz;
+
+ return (ptr);
+}
diff --git a/usr.bin/xlint/lint2/msg.c b/usr.bin/xlint/lint2/msg.c
new file mode 100644
index 0000000..c55ba96
--- /dev/null
+++ b/usr.bin/xlint/lint2/msg.c
@@ -0,0 +1,157 @@
+/* $NetBSD: msg.c,v 1.2 1995/07/03 21:24:56 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: msg.c,v 1.2 1995/07/03 21:24:56 cgd Exp $";
+#endif
+
+#include <string.h>
+
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "lint2.h"
+
+
+static const char *msgs[] = {
+ "%s used( %s ), but not defined", /* 0 */
+ "%s defined( %s ), but never used", /* 1 */
+ "%s declared( %s ), but never used or defined", /* 2 */
+ "%s multiply defined \t%s :: %s", /* 3 */
+ "%s value used inconsistently \t%s :: %s", /* 4 */
+ "%s value declared inconsistently \t%s :: %s", /* 5 */
+ "%s, arg %d used inconsistently \t%s :: %s", /* 6 */
+ "%s: variable # of args \t%s :: %s", /* 7 */
+ "%s returns value which is always ignored", /* 8 */
+ "%s returns value which is sometimes ignored", /* 9 */
+ "%s value is used( %s ), but none returned", /* 10 */
+ "%s, arg %d declared inconsistently \t%s :: %s", /* 11 */
+ "%s: variable # of args declared \t%s :: %s", /* 12 */
+ "%s: malformed format string \t%s", /* 13 */
+ "%s, arg %d inconsistent with format \t%s", /* 14 */
+ "%s: too few args for format \t%s", /* 15 */
+ "%s: too many args for format \t%s", /* 16 */
+ "%s function value must be declared before use \t%s :: %s",/* 17 */
+};
+
+static const char *basename __P((const char *));
+
+#ifdef __STDC__
+void
+msg(int n, ...)
+{
+#else
+void
+msg(va_alist)
+ va_dcl
+ int n;
+{
+#endif
+ va_list ap;
+
+#ifdef __STDC__
+ va_start(ap, n);
+#else
+ va_start(ap);
+ n = va_arg(ap, int);
+#endif
+
+ (void)vprintf(msgs[n], ap);
+ (void)printf("\n");
+
+ va_end(ap);
+}
+
+/*
+ * Return a pointer to the last component of a path.
+ */
+static const char *
+basename(path)
+ const char *path;
+{
+ const char *cp, *cp1, *cp2;
+
+ if (Fflag)
+ return (path);
+
+ cp = cp1 = cp2 = path;
+ while (*cp != '\0') {
+ if (*cp++ == '/') {
+ cp2 = cp1;
+ cp1 = cp;
+ }
+ }
+ return (*cp1 == '\0' ? cp2 : cp1);
+}
+
+/*
+ * Create a string which describes a position in a source file.
+ */
+const char *
+mkpos(posp)
+ pos_t *posp;
+{
+ size_t len;
+ const char *fn;
+ static char *buf;
+ static size_t blen = 0;
+ int qm, src, line;
+
+ if (Hflag && posp->p_src != posp->p_isrc) {
+ src = posp->p_isrc;
+ line = posp->p_iline;
+ } else {
+ src = posp->p_src;
+ line = posp->p_line;
+ }
+ qm = !Hflag && posp->p_src != posp->p_isrc;
+
+ len = strlen(fn = basename(fnames[src]));
+ len += 3 * sizeof (u_short) + 4;
+
+ if (len > blen)
+ buf = xrealloc(buf, blen = len);
+ if (line != 0) {
+ (void)sprintf(buf, "%s%s(%hu)",
+ fn, qm ? "?" : "", line);
+ } else {
+ (void)sprintf(buf, "%s", fn);
+ }
+
+ return (buf);
+}
+
diff --git a/usr.bin/xlint/lint2/read.c b/usr.bin/xlint/lint2/read.c
new file mode 100644
index 0000000..6498a5c
--- /dev/null
+++ b/usr.bin/xlint/lint2/read.c
@@ -0,0 +1,1135 @@
+/* $NetBSD: read.c,v 1.2 1995/07/03 21:24:59 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: read.c,v 1.2 1995/07/03 21:24:59 cgd Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <err.h>
+
+#include "lint2.h"
+
+
+/* index of current (included) source file */
+static int srcfile;
+
+/*
+ * The array pointed to by inpfns maps the file name indices of input files
+ * to the file name indices used in lint2
+ */
+static short *inpfns;
+static size_t ninpfns;
+
+/*
+ * The array pointed to by *fnames maps file name indizes to file names.
+ * Indices of type short are used instead of pointers to save memory.
+ */
+const char **fnames;
+static size_t nfnames;
+
+/*
+ * Types are shared (to save memory for the types itself) and accessed
+ * via indices (to save memory for references to types (indices are short)).
+ * To share types, a equal type must be located fast. This is done by a
+ * hash table. Access by indices is done via an array of pointers to the
+ * types.
+ */
+typedef struct thtab {
+ const char *th_name;
+ u_short th_idx;
+ struct thtab *th_nxt;
+} thtab_t;
+static thtab_t **thtab; /* hash table */
+type_t **tlst; /* array for indexed access */
+static size_t tlstlen; /* length of tlst */
+
+/* index of current C source file (as spezified at the command line) */
+static int csrcfile;
+
+
+static void inperr __P((void));
+static void setsrc __P((const char *));
+static void setfnid __P((int, const char *));
+static void funccall __P((pos_t *, const char *));
+static void decldef __P((pos_t *, const char *));
+static void usedsym __P((pos_t *, const char *));
+static u_short inptype __P((const char *, const char **));
+static int gettlen __P((const char *, const char **));
+static u_short findtype __P((const char *, size_t, int));
+static u_short storetyp __P((type_t *, const char *, size_t, int));
+static int thash __P((const char *, size_t));
+static char *inpqstrg __P((const char *, const char **));
+static const char *inpname __P((const char *, const char **));
+static int getfnidx __P((const char *));
+
+void
+readfile(name)
+ const char *name;
+{
+ FILE *inp;
+ size_t len;
+ const char *cp;
+ char *line, *eptr, rt;
+ int cline, isrc, iline;
+ pos_t pos;
+
+ if (inpfns == NULL)
+ inpfns = xcalloc(ninpfns = 128, sizeof (short));
+ if (fnames == NULL)
+ fnames = xcalloc(nfnames = 256, sizeof (char *));
+ if (tlstlen == 0)
+ tlst = xcalloc(tlstlen = 256, sizeof (type_t *));
+ if (thtab == NULL)
+ thtab = xcalloc(THSHSIZ2, sizeof (thtab_t));
+
+ srcfile = getfnidx(name);
+
+ if ((inp = fopen(name, "r")) == NULL)
+ err(1, "cannot open %s", name);
+
+ while ((line = fgetln(inp, &len)) != NULL) {
+
+ if (len == 0 || line[len - 1] != '\n')
+ inperr();
+ line[len - 1] = '\0';
+ cp = line;
+
+ /* line number in csrcfile */
+ cline = (int)strtol(cp, &eptr, 10);
+ if (cp == eptr) {
+ cline = -1;
+ } else {
+ cp = eptr;
+ }
+
+ /* record type */
+ if (*cp != '\0') {
+ rt = *cp++;
+ } else {
+ inperr();
+ }
+
+ if (rt == 'S') {
+ setsrc(cp);
+ continue;
+ } else if (rt == 's') {
+ setfnid(cline, cp);
+ continue;
+ }
+
+ /*
+ * Index of (included) source file. If this index is
+ * different from csrcfile, it refers to an included
+ * file.
+ */
+ isrc = (int)strtol(cp, &eptr, 10);
+ if (cp == eptr)
+ inperr();
+ cp = eptr;
+ isrc = inpfns[isrc];
+
+ /* line number in isrc */
+ if (*cp++ != '.')
+ inperr();
+ iline = (int)strtol(cp, &eptr, 10);
+ if (cp == eptr)
+ inperr();
+ cp = eptr;
+
+ pos.p_src = (u_short)csrcfile;
+ pos.p_line = (u_short)cline;
+ pos.p_isrc = (u_short)isrc;
+ pos.p_iline = (u_short)iline;
+
+ /* process rest of this record */
+ switch (rt) {
+ case 'c':
+ funccall(&pos, cp);
+ break;
+ case 'd':
+ decldef(&pos, cp);
+ break;
+ case 'u':
+ usedsym(&pos, cp);
+ break;
+ default:
+ inperr();
+ }
+
+ }
+
+ if (ferror(inp))
+ err(1, "read error on %s", name);
+
+ (void)fclose(inp);
+}
+
+
+static void
+inperr()
+{
+ errx(1, "input file error: %s", fnames[srcfile]);
+}
+
+/*
+ * Set the name of the C source file of the .ln file which is
+ * currently read.
+ */
+static void
+setsrc(cp)
+ const char *cp;
+{
+ csrcfile = getfnidx(cp);
+}
+
+/*
+ * setfnid() gets as input an index as used in an input file and the
+ * associated file name. If neccessary, it creates a new lint2 file
+ * name index for this file name and creates the mapping of the index
+ * as used in the input file to the index used in lint2.
+ */
+static void
+setfnid(fid, cp)
+ int fid;
+ const char *cp;
+{
+ if (fid == -1)
+ inperr();
+
+ if (fid >= ninpfns) {
+ inpfns = xrealloc(inpfns, (ninpfns * 2) * sizeof (short));
+ (void)memset(inpfns + ninpfns, 0, ninpfns * sizeof (short));
+ ninpfns *= 2;
+ }
+ /*
+ * Should always be true because indices written in the output
+ * file by lint1 are always the previous index + 1.
+ */
+ if (fid >= ninpfns)
+ errx(1, "internal error: setfnid()");
+ inpfns[fid] = (u_short)getfnidx(cp);
+}
+
+/*
+ * Process a function call record (c-record).
+ */
+static void
+funccall(posp, cp)
+ pos_t *posp;
+ const char *cp;
+{
+ arginf_t *ai, **lai;
+ char c, *eptr;
+ int rused, rdisc;
+ hte_t *hte;
+ fcall_t *fcall;
+
+ fcall = xalloc(sizeof (fcall_t));
+ STRUCT_ASSIGN(fcall->f_pos, *posp);
+
+ /* read flags */
+ rused = rdisc = 0;
+ lai = &fcall->f_args;
+ while ((c = *cp) == 'u' || c == 'i' || c == 'd' ||
+ c == 'z' || c == 'p' || c == 'n' || c == 's') {
+ cp++;
+ switch (c) {
+ case 'u':
+ if (rused || rdisc)
+ inperr();
+ rused = 1;
+ break;
+ case 'i':
+ if (rused || rdisc)
+ inperr();
+ break;
+ case 'd':
+ if (rused || rdisc)
+ inperr();
+ rdisc = 1;
+ break;
+ case 'z':
+ case 'p':
+ case 'n':
+ case 's':
+ ai = xalloc(sizeof (arginf_t));
+ ai->a_num = (int)strtol(cp, &eptr, 10);
+ if (cp == eptr)
+ inperr();
+ cp = eptr;
+ if (c == 'z') {
+ ai->a_pcon = ai->a_zero = 1;
+ } else if (c == 'p') {
+ ai->a_pcon = 1;
+ } else if (c == 'n') {
+ ai->a_ncon = 1;
+ } else {
+ ai->a_fmt = 1;
+ ai->a_fstrg = inpqstrg(cp, &cp);
+ }
+ *lai = ai;
+ lai = &ai->a_nxt;
+ break;
+ }
+ }
+ fcall->f_rused = rused;
+ fcall->f_rdisc = rdisc;
+
+ /* read name of function */
+ hte = hsearch(inpname(cp, &cp), 1);
+ hte->h_used = 1;
+
+ fcall->f_type = inptype(cp, &cp);
+
+ *hte->h_lcall = fcall;
+ hte->h_lcall = &fcall->f_nxt;
+
+ if (*cp != '\0')
+ inperr();
+}
+
+/*
+ * Process a declaration or definition (d-record).
+ */
+static void
+decldef(posp, cp)
+ pos_t *posp;
+ const char *cp;
+{
+ sym_t *symp, sym;
+ char c, *ep;
+ int used;
+ hte_t *hte;
+
+ (void)memset(&sym, 0, sizeof (sym));
+ STRUCT_ASSIGN(sym.s_pos, *posp);
+ sym.s_def = NODECL;
+
+ used = 0;
+
+ while ((c = *cp) == 't' || c == 'd' || c == 'e' || c == 'u' ||
+ c == 'r' || c == 'o' || c == 's' || c == 'v' ||
+ c == 'P' || c == 'S') {
+ cp++;
+ switch (c) {
+ case 't':
+ if (sym.s_def != NODECL)
+ inperr();
+ sym.s_def = TDEF;
+ break;
+ case 'd':
+ if (sym.s_def != NODECL)
+ inperr();
+ sym.s_def = DEF;
+ break;
+ case 'e':
+ if (sym.s_def != NODECL)
+ inperr();
+ sym.s_def = DECL;
+ break;
+ case 'u':
+ if (used)
+ inperr();
+ used = 1;
+ break;
+ case 'r':
+ if (sym.s_rval)
+ inperr();
+ sym.s_rval = 1;
+ break;
+ case 'o':
+ if (sym.s_osdef)
+ inperr();
+ sym.s_osdef = 1;
+ break;
+ case 's':
+ if (sym.s_static)
+ inperr();
+ sym.s_static = 1;
+ break;
+ case 'v':
+ if (sym.s_va)
+ inperr();
+ sym.s_va = 1;
+ sym.s_nva = (short)strtol(cp, &ep, 10);
+ if (cp == ep)
+ inperr();
+ cp = ep;
+ break;
+ case 'P':
+ if (sym.s_prfl)
+ inperr();
+ sym.s_prfl = 1;
+ sym.s_nprfl = (short)strtol(cp, &ep, 10);
+ if (cp == ep)
+ inperr();
+ cp = ep;
+ break;
+ case 'S':
+ if (sym.s_scfl)
+ inperr();
+ sym.s_scfl = 1;
+ sym.s_nscfl = (short)strtol(cp, &ep, 10);
+ if (cp == ep)
+ inperr();
+ cp = ep;
+ break;
+ }
+ }
+
+ /* read symbol name */
+ hte = hsearch(inpname(cp, &cp), 1);
+ hte->h_used |= used;
+ if (sym.s_def == DEF || sym.s_def == TDEF)
+ hte->h_def = 1;
+
+ sym.s_type = inptype(cp, &cp);
+
+ /*
+ * Allocate memory for this symbol only if it was not already
+ * declared or tentatively defined at the same location with
+ * the same type. Works only for symbols with external linkage,
+ * because static symbols, tentatively defined at the same location
+ * but in different translation units are really different symbols.
+ */
+ for (symp = hte->h_syms; symp != NULL; symp = symp->s_nxt) {
+ if (symp->s_pos.p_isrc == sym.s_pos.p_isrc &&
+ symp->s_pos.p_iline == sym.s_pos.p_iline &&
+ symp->s_type == sym.s_type &&
+ ((symp->s_def == DECL && sym.s_def == DECL) ||
+ (!sflag && symp->s_def == TDEF && sym.s_def == TDEF)) &&
+ !symp->s_static && !sym.s_static) {
+ break;
+ }
+ }
+
+ if (symp == NULL) {
+ /* allocsym reserviert keinen Platz fuer s_nva */
+ if (sym.s_va || sym.s_prfl || sym.s_scfl) {
+ symp = xalloc(sizeof (sym_t));
+ STRUCT_ASSIGN(*symp, sym);
+ } else {
+ symp = xalloc(sizeof (symp->s_s));
+ STRUCT_ASSIGN(symp->s_s, sym.s_s);
+ }
+ *hte->h_lsym = symp;
+ hte->h_lsym = &symp->s_nxt;
+ }
+
+ if (*cp != '\0')
+ inperr();
+}
+
+/*
+ * Read an u-record (emited by lint1 if a symbol was used).
+ */
+static void
+usedsym(posp, cp)
+ pos_t *posp;
+ const char *cp;
+{
+ usym_t *usym;
+ hte_t *hte;
+
+ usym = xalloc(sizeof (usym_t));
+ STRUCT_ASSIGN(usym->u_pos, *posp);
+
+ /* needed as delimiter between two numbers */
+ if (*cp++ != 'x')
+ inperr();
+
+ hte = hsearch(inpname(cp, &cp), 1);
+ hte->h_used = 1;
+
+ *hte->h_lusym = usym;
+ hte->h_lusym = &usym->u_nxt;
+}
+
+/*
+ * Read a type and return the index of this type.
+ */
+static u_short
+inptype(cp, epp)
+ const char *cp, **epp;
+{
+ char c, s, *eptr;
+ const char *ep;
+ type_t *tp;
+ int narg, i, osdef;
+ size_t tlen;
+ u_short tidx;
+ int h;
+
+ /* If we have this type already, return it's index. */
+ tlen = gettlen(cp, &ep);
+ h = thash(cp, tlen);
+ if ((tidx = findtype(cp, tlen, h)) != 0) {
+ *epp = ep;
+ return (tidx);
+ }
+
+ /* No, we must create a new type. */
+ tp = xalloc(sizeof (type_t));
+
+ tidx = storetyp(tp, cp, tlen, h);
+
+ c = *cp++;
+
+ while (c == 'c' || c == 'v') {
+ if (c == 'c') {
+ tp->t_const = 1;
+ } else {
+ tp->t_volatile = 1;
+ }
+ c = *cp++;
+ }
+
+ if (c == 's' || c == 'u' || c == 'l' || c == 'e') {
+ s = c;
+ c = *cp++;
+ } else {
+ s = '\0';
+ }
+
+ switch (c) {
+ case 'C':
+ tp->t_tspec = s == 's' ? SCHAR : (s == 'u' ? UCHAR : CHAR);
+ break;
+ case 'S':
+ tp->t_tspec = s == 'u' ? USHORT : SHORT;
+ break;
+ case 'I':
+ tp->t_tspec = s == 'u' ? UINT : INT;
+ break;
+ case 'L':
+ tp->t_tspec = s == 'u' ? ULONG : LONG;
+ break;
+ case 'Q':
+ tp->t_tspec = s == 'u' ? UQUAD : QUAD;
+ break;
+ case 'D':
+ tp->t_tspec = s == 's' ? FLOAT : (s == 'l' ? LDOUBLE : DOUBLE);
+ break;
+ case 'V':
+ tp->t_tspec = VOID;
+ break;
+ case 'P':
+ tp->t_tspec = PTR;
+ break;
+ case 'A':
+ tp->t_tspec = ARRAY;
+ break;
+ case 'F':
+ case 'f':
+ osdef = c == 'f';
+ tp->t_tspec = FUNC;
+ break;
+ case 'T':
+ tp->t_tspec = s == 'e' ? ENUM : (s == 's' ? STRUCT : UNION);
+ break;
+ }
+
+ switch (tp->t_tspec) {
+ case ARRAY:
+ tp->t_dim = (int)strtol(cp, &eptr, 10);
+ cp = eptr;
+ tp->t_subt = TP(inptype(cp, &cp));
+ break;
+ case PTR:
+ tp->t_subt = TP(inptype(cp, &cp));
+ break;
+ case FUNC:
+ c = *cp;
+ if (isdigit((u_char)c)) {
+ if (!osdef)
+ tp->t_proto = 1;
+ narg = (int)strtol(cp, &eptr, 10);
+ cp = eptr;
+ tp->t_args = xcalloc((size_t)(narg + 1),
+ sizeof (type_t *));
+ for (i = 0; i < narg; i++) {
+ if (i == narg - 1 && *cp == 'E') {
+ tp->t_vararg = 1;
+ cp++;
+ } else {
+ tp->t_args[i] = TP(inptype(cp, &cp));
+ }
+ }
+ }
+ tp->t_subt = TP(inptype(cp, &cp));
+ break;
+ case ENUM:
+ tp->t_tspec = INT;
+ tp->t_isenum = 1;
+ /* FALLTHROUGH */
+ case STRUCT:
+ case UNION:
+ switch (*cp++) {
+ case '0':
+ break;
+ case '1':
+ tp->t_istag = 1;
+ tp->t_tag = hsearch(inpname(cp, &cp), 1);
+ break;
+ case '2':
+ tp->t_istynam = 1;
+ tp->t_tynam = hsearch(inpname(cp, &cp), 1);
+ break;
+ }
+ break;
+ /* LINTED (enumeration value(s) not handled in switch) */
+ default:
+ }
+
+ *epp = cp;
+ return (tidx);
+}
+
+/*
+ * Get the length of a type string.
+ */
+static int
+gettlen(cp, epp)
+ const char *cp, **epp;
+{
+ const char *cp1;
+ char c, s, *eptr;
+ tspec_t t;
+ int narg, i, cm, vm;
+
+ cp1 = cp;
+
+ c = *cp++;
+
+ cm = vm = 0;
+
+ while (c == 'c' || c == 'v') {
+ if (c == 'c') {
+ if (cm)
+ inperr();
+ cm = 1;
+ } else {
+ if (vm)
+ inperr();
+ vm = 1;
+ }
+ c = *cp++;
+ }
+
+ if (c == 's' || c == 'u' || c == 'l' || c == 'e') {
+ s = c;
+ c = *cp++;
+ } else {
+ s = '\0';
+ }
+
+ t = NOTSPEC;
+
+ switch (c) {
+ case 'C':
+ if (s == 's') {
+ t = SCHAR;
+ } else if (s == 'u') {
+ t = UCHAR;
+ } else if (s == '\0') {
+ t = CHAR;
+ }
+ break;
+ case 'S':
+ if (s == 'u') {
+ t = USHORT;
+ } else if (s == '\0') {
+ t = SHORT;
+ }
+ break;
+ case 'I':
+ if (s == 'u') {
+ t = UINT;
+ } else if (s == '\0') {
+ t = INT;
+ }
+ break;
+ case 'L':
+ if (s == 'u') {
+ t = ULONG;
+ } else if (s == '\0') {
+ t = LONG;
+ }
+ break;
+ case 'Q':
+ if (s == 'u') {
+ t = UQUAD;
+ } else if (s == '\0') {
+ t = QUAD;
+ }
+ break;
+ case 'D':
+ if (s == 's') {
+ t = FLOAT;
+ } else if (s == 'l') {
+ t = LDOUBLE;
+ } else if (s == '\0') {
+ t = DOUBLE;
+ }
+ break;
+ case 'V':
+ if (s == '\0')
+ t = VOID;
+ break;
+ case 'P':
+ if (s == '\0')
+ t = PTR;
+ break;
+ case 'A':
+ if (s == '\0')
+ t = ARRAY;
+ break;
+ case 'F':
+ case 'f':
+ if (s == '\0')
+ t = FUNC;
+ break;
+ case 'T':
+ if (s == 'e') {
+ t = ENUM;
+ } else if (s == 's') {
+ t = STRUCT;
+ } else if (s == 'u') {
+ t = UNION;
+ }
+ break;
+ default:
+ inperr();
+ }
+
+ if (t == NOTSPEC)
+ inperr();
+
+ switch (t) {
+ case ARRAY:
+ (void)strtol(cp, &eptr, 10);
+ if (cp == eptr)
+ inperr();
+ cp = eptr;
+ (void)gettlen(cp, &cp);
+ break;
+ case PTR:
+ (void)gettlen(cp, &cp);
+ break;
+ case FUNC:
+ c = *cp;
+ if (isdigit((u_char)c)) {
+ narg = (int)strtol(cp, &eptr, 10);
+ cp = eptr;
+ for (i = 0; i < narg; i++) {
+ if (i == narg - 1 && *cp == 'E') {
+ cp++;
+ } else {
+ (void)gettlen(cp, &cp);
+ }
+ }
+ }
+ (void)gettlen(cp, &cp);
+ break;
+ case ENUM:
+ case STRUCT:
+ case UNION:
+ switch (*cp++) {
+ case '0':
+ break;
+ case '1':
+ (void)inpname(cp, &cp);
+ break;
+ case '2':
+ (void)inpname(cp, &cp);
+ break;
+ default:
+ inperr();
+ }
+ break;
+ /* LINTED (enumeration value(s) not handled in switch) */
+ default:
+ }
+
+ *epp = cp;
+ return (cp - cp1);
+}
+
+/*
+ * Search a type by it's type string.
+ */
+static u_short
+findtype(cp, len, h)
+ const char *cp;
+ size_t len;
+ int h;
+{
+ thtab_t *thte;
+
+ for (thte = thtab[h]; thte != NULL; thte = thte->th_nxt) {
+ if (strncmp(thte->th_name, cp, len) != 0)
+ continue;
+ if (thte->th_name[len] == '\0')
+ return (thte->th_idx);
+ }
+
+ return (0);
+}
+
+/*
+ * Store a type and it's type string so we can later share this type
+ * if we read the same type string from the input file.
+ */
+static u_short
+storetyp(tp, cp, len, h)
+ type_t *tp;
+ const char *cp;
+ size_t len;
+ int h;
+{
+ /* 0 ist reserved */
+ static u_int tidx = 1;
+ thtab_t *thte;
+ char *name;
+
+ if (tidx >= USHRT_MAX)
+ errx(1, "sorry, too many types");
+
+ if (tidx == tlstlen - 1) {
+ tlst = xrealloc(tlst, (tlstlen * 2) * sizeof (type_t *));
+ (void)memset(tlst + tlstlen, 0, tlstlen * sizeof (type_t *));
+ tlstlen *= 2;
+ }
+
+ tlst[tidx] = tp;
+
+ /* create a hash table entry */
+ name = xalloc(len + 1);
+ (void)memcpy(name, cp, len);
+ name[len] = '\0';
+
+ thte = xalloc(sizeof (thtab_t));
+ thte->th_name = name;
+ thte->th_idx = tidx;
+ thte->th_nxt = thtab[h];
+ thtab[h] = thte;
+
+ return ((u_short)tidx++);
+}
+
+/*
+ * Hash function for types
+ */
+static int
+thash(s, len)
+ const char *s;
+ size_t len;
+{
+ u_int v;
+
+ v = 0;
+ while (len-- != 0) {
+ v = (v << sizeof (v)) + (u_char)*s++;
+ v ^= v >> (sizeof (v) * CHAR_BIT - sizeof (v));
+ }
+ return (v % THSHSIZ2);
+}
+
+/*
+ * Read a string enclosed by "". This string may contain quoted chars.
+ */
+static char *
+inpqstrg(src, epp)
+ const char *src, **epp;
+{
+ char *strg, *dst;
+ size_t slen;
+ int c;
+ int v;
+
+ dst = strg = xmalloc(slen = 32);
+
+ if ((c = *src++) != '"')
+ inperr();
+ if ((c = *src++) == '\0')
+ inperr();
+
+ while (c != '"') {
+ if (c == '\\') {
+ if ((c = *src++) == '\0')
+ inperr();
+ switch (c) {
+ case 'n':
+ c = '\n';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+#ifdef __STDC__
+ c = '\v';
+#else
+ c = '\013';
+#endif
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'a':
+#ifdef __STDC__
+ c = '\a';
+#else
+ c = '\007';
+#endif
+ break;
+ case '\\':
+ c = '\\';
+ break;
+ case '"':
+ c = '"';
+ break;
+ case '\'':
+ c = '\'';
+ break;
+ case '0': case '1': case '2': case '3':
+ v = (c - '0') << 6;
+ if ((c = *src++) < '0' || c > '7')
+ inperr();
+ v |= (c - '0') << 3;
+ if ((c = *src++) < '0' || c > '7')
+ inperr();
+ v |= c - '0';
+ c = (u_char)v;
+ break;
+ default:
+ inperr();
+ }
+ }
+ /* keep space for trailing '\0' */
+ if (dst - strg == slen - 1) {
+ strg = xrealloc(strg, slen * 2);
+ dst = strg + (slen - 1);
+ slen *= 2;
+ }
+ *dst++ = (char)c;
+ if ((c = *src++) == '\0')
+ inperr();
+ }
+ *dst = '\0';
+
+ *epp = src;
+ return (strg);
+}
+
+/*
+ * Read the name of a symbol in static memory.
+ */
+static const char *
+inpname(cp, epp)
+ const char *cp, **epp;
+{
+ static char *buf;
+ static size_t blen = 0;
+ size_t len, i;
+ char *eptr, c;
+
+ len = (int)strtol(cp, &eptr, 10);
+ if (cp == eptr)
+ inperr();
+ cp = eptr;
+ if (len + 1 > blen)
+ buf = xrealloc(buf, blen = len + 1);
+ for (i = 0; i < len; i++) {
+ c = *cp++;
+ if (!isalnum(c) && c != '_')
+ inperr();
+ buf[i] = c;
+ }
+ buf[i] = '\0';
+
+ *epp = cp;
+ return (buf);
+}
+
+/*
+ * Return the index of a file name. If the name cannot be found, create
+ * a new entry and return the index of the newly created entry.
+ */
+static int
+getfnidx(fn)
+ const char *fn;
+{
+ int i;
+
+ /* 0 ist reserved */
+ for (i = 1; fnames[i] != NULL; i++) {
+ if (strcmp(fnames[i], fn) == 0)
+ break;
+ }
+ if (fnames[i] != NULL)
+ return (i);
+
+ if (i == nfnames - 1) {
+ fnames = xrealloc(fnames, (nfnames * 2) * sizeof (char *));
+ (void)memset(fnames + nfnames, 0, nfnames * sizeof (char *));
+ nfnames *= 2;
+ }
+
+ fnames[i] = xstrdup(fn);
+ return (i);
+}
+
+/*
+ * Separate symbols with static and external linkage.
+ */
+void
+mkstatic(hte)
+ hte_t *hte;
+{
+ sym_t *sym1, **symp, *sym;
+ fcall_t **callp, *call;
+ usym_t **usymp, *usym;
+ hte_t *nhte;
+ int ofnd;
+
+ /* Look for first static definition */
+ for (sym1 = hte->h_syms; sym1 != NULL; sym1 = sym1->s_nxt) {
+ if (sym1->s_static)
+ break;
+ }
+ if (sym1 == NULL)
+ return;
+
+ /* Do nothing if this name is used only in one translation unit. */
+ ofnd = 0;
+ for (sym = hte->h_syms; sym != NULL && !ofnd; sym = sym->s_nxt) {
+ if (sym->s_pos.p_src != sym1->s_pos.p_src)
+ ofnd = 1;
+ }
+ for (call = hte->h_calls; call != NULL && !ofnd; call = call->f_nxt) {
+ if (call->f_pos.p_src != sym1->s_pos.p_src)
+ ofnd = 1;
+ }
+ for (usym = hte->h_usyms; usym != NULL && !ofnd; usym = usym->u_nxt) {
+ if (usym->u_pos.p_src != sym1->s_pos.p_src)
+ ofnd = 1;
+ }
+ if (!ofnd) {
+ hte->h_used = 1;
+ /* errors about undef. static symbols are printed in lint1 */
+ hte->h_def = 1;
+ hte->h_static = 1;
+ return;
+ }
+
+ /*
+ * Create a new hash table entry
+ *
+ * XXX this entry should be put at the beginning of the list to
+ * avoid to process the same symbol twice.
+ */
+ for (nhte = hte; nhte->h_link != NULL; nhte = nhte->h_link) ;
+ nhte->h_link = xalloc(sizeof (hte_t));
+ nhte = nhte->h_link;
+ nhte->h_name = hte->h_name;
+ nhte->h_static = 1;
+ nhte->h_used = 1;
+ nhte->h_def = 1; /* error in lint1 */
+ nhte->h_lsym = &nhte->h_syms;
+ nhte->h_lcall = &nhte->h_calls;
+ nhte->h_lusym = &nhte->h_usyms;
+
+ /*
+ * move all symbols used in this translation unit into the new
+ * hash table entry.
+ */
+ for (symp = &hte->h_syms; (sym = *symp) != NULL; ) {
+ if (sym->s_pos.p_src == sym1->s_pos.p_src) {
+ sym->s_static = 1;
+ (*symp) = sym->s_nxt;
+ if (hte->h_lsym == &sym->s_nxt)
+ hte->h_lsym = symp;
+ sym->s_nxt = NULL;
+ *nhte->h_lsym = sym;
+ nhte->h_lsym = &sym->s_nxt;
+ } else {
+ symp = &sym->s_nxt;
+ }
+ }
+ for (callp = &hte->h_calls; (call = *callp) != NULL; ) {
+ if (call->f_pos.p_src == sym1->s_pos.p_src) {
+ (*callp) = call->f_nxt;
+ if (hte->h_lcall == &call->f_nxt)
+ hte->h_lcall = callp;
+ call->f_nxt = NULL;
+ *nhte->h_lcall = call;
+ nhte->h_lcall = &call->f_nxt;
+ } else {
+ callp = &call->f_nxt;
+ }
+ }
+ for (usymp = &hte->h_usyms; (usym = *usymp) != NULL; ) {
+ if (usym->u_pos.p_src == sym1->s_pos.p_src) {
+ (*usymp) = usym->u_nxt;
+ if (hte->h_lusym == &usym->u_nxt)
+ hte->h_lusym = usymp;
+ usym->u_nxt = NULL;
+ *nhte->h_lusym = usym;
+ nhte->h_lusym = &usym->u_nxt;
+ } else {
+ usymp = &usym->u_nxt;
+ }
+ }
+
+ /* h_def must be recalculated for old hte */
+ hte->h_def = nhte->h_def = 0;
+ for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) {
+ if (sym->s_def == DEF || sym->s_def == TDEF) {
+ hte->h_def = 1;
+ break;
+ }
+ }
+
+ mkstatic(hte);
+}
diff --git a/usr.bin/xlint/llib/Makefile b/usr.bin/xlint/llib/Makefile
new file mode 100644
index 0000000..bf87315
--- /dev/null
+++ b/usr.bin/xlint/llib/Makefile
@@ -0,0 +1,21 @@
+# $NetBSD: Makefile,v 1.2 1995/07/03 21:25:05 cgd Exp $
+
+#LIBS= llib-lposix.ln llib-lstdc.ln
+LIBS= llib-lstdc.ln
+
+all: ${LIBS}
+
+install:
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${LIBMODE} \
+ ${LIBS} ${DESTDIR}${LINTLIBDIR}
+
+clean cleanall:
+ rm -f ${LIBS}
+
+llib-lposix.ln: llib-lposix
+ lint -Cposix ${.ALLSRC}
+
+llib-lstdc.ln: llib-lstdc
+ lint -Cc ${.ALLSRC}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/xlint/llib/llib-lposix b/usr.bin/xlint/llib/llib-lposix
new file mode 100644
index 0000000..db1b3cf
--- /dev/null
+++ b/usr.bin/xlint/llib/llib-lposix
@@ -0,0 +1,311 @@
+/* $NetBSD: llib-lposix,v 1.2 1995/07/03 21:25:09 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+/* LINTLIBRARY */
+
+#define _POSIX_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/times.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <assert.h>
+#include <termios.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <ctype.h>
+#include <signal.h>
+#include <locale.h>
+#include <setjmp.h>
+#include <string.h>
+#include <utime.h>
+
+
+/* PROTOLIB1 */
+
+
+void (abort)(void);
+int (abs)(int j);
+int (access)(const char *path, int amode);
+double (acos)(double x);
+unsigned (alarm)(unsigned seconds);
+char *(asctime)(const struct tm *timeptr);
+double (asin)(double x);
+void (__assert)(const char *expression, int line, const char *file);
+double (atan)(double x);
+double (atan2)(double y, double x);
+int (atexit)(void (*func)(void));
+double (atof)(const char *nptr);
+int (atoi)(const char *nptr);
+long (atol)(const char *nptr);
+void *(bsearch)(const void *key, const void *base, size_t nmemb,
+ size_t size, int (*compar)(const void *, const void *));
+void *(calloc)(size_t nmemb, size_t size);
+double (ceil)(double x);
+speed_t (cfgetispeed)(const struct termios *p);
+speed_t (cfgetospeed)(const struct termios *p);
+int (cfsetispeed)(struct termios *p, speed_t speed);
+int (cfsetospeed)(struct termios *p, speed_t speed);
+int (chdir)(const char *path);
+int (chmod)(const char *path, mode_t mode);
+int (chown)(const char *path, uid_t owner, gid_t group);
+void (clearerr)(FILE *stream);
+clock_t (clock)(void);
+int (close)(int fildes);
+int (closedir)(DIR *dirp);
+double (cos)(double x);
+double (cosh)(double x);
+int (creat)(const char *path, mode_t mode);
+char *(ctermid)(char *s);
+char *(ctime)(const time_t *timer);
+char *(cuserid)(char *s);
+double (difftime)(time_t time1, time_t time0);
+div_t (div)(int numer, int denom);
+int (dup)(int fildes);
+int (dup2)(int fildes, int fildes2);
+int (errno);
+int (execl)(const char *path, const char *arg, ...);
+int (execle)(const char *path, const char *arg, ...);
+int (execlp)(const char *file, const char *arg, ...);
+int (execv)(const char *path, char *const argv[]);
+int (execve)(const char *path, char *const argv[], char *const *envp);
+int (execvp)(const char *file, char *const argv[]);
+void (exit)(int status);
+void (_exit)(int status);
+double (exp)(double x);
+double (fabs)(double x);
+int (fclose)(FILE *stream);
+int (fcntl)(int fildes, int cmd, ...);
+FILE *(fdopen)(int fildes, const char *type);
+int (feof)(FILE *stream);
+int (ferror)(FILE *stream);
+int (fflush)(FILE *stream);
+int (fgetc)(FILE *stream);
+int (fgetpos)(FILE *stream, fpos_t *pos);
+char *(fgets)(char *s, int n, FILE *stream);
+int (fileno)(FILE *stream);
+double (floor)(double x);
+double (fmod)(double x, double y);
+FILE *(fopen)(const char *filename, const char *mode);
+pid_t (fork)(void);
+long (fpathconf)(int fildes, int name);
+/* PRINTFLIKE2 */
+int (fprintf)(FILE *stream, const char *format, ...);
+int (fputc)(int c, FILE *stream);
+int (fputs)(const char *s, FILE *stream);
+size_t (fread)(void *ptr, size_t size, size_t nmemb, FILE *stream);
+void (free)(void *ptr);
+FILE *(freopen)(const char *filename, const char *mode, FILE *stream);
+double (frepx)(double value, int *exp);
+/* SCANFLIKE2 */
+int (fscanf)(FILE *stream, const char *format, ...);
+int (fseek)(FILE *stream, long int offset, int whence);
+int (fsetpos)(FILE *stream, const fpos_t *pos);
+int (fstat)(int fildes, struct stat *buf);
+long (ftell)(FILE *stream);
+size_t (fwrite)(const void *ptr, size_t size, size_t nmemb, FILE *stream);
+int (getc)(FILE *stream);
+int (getchar)(void);
+char *(getcwd)(char *buf, size_t size);
+gid_t (getegid)(void);
+char *(getenv)(const char *name);
+uid_t (geteuid)(void);
+gid_t (getgid)(void);
+struct group *(getgrgid)(gid_t gid);
+struct group *(getgrnam)(const char *name);
+int (getgroups)(int gidsetsize, gid_t grouplist[]);
+char *(getlogin)(void);
+pid_t (getpgrp)(void);
+pid_t (getpid)(void);
+pid_t (getppid)(void);
+struct passwd *(getpwnam)(const char *name);
+struct passwd *(getpwuid)(uid_t uid);
+char *(gets)(char *s);
+uid_t (getuid)(void);
+struct tm *(gmtime)(const time_t *timer);
+int (isalnum)(int c);
+int (isalpha)(int c);
+int (isatty)(int fildes);
+int (iscntrl)(int c);
+int (isdigit)(int c);
+int (isgraph)(int c);
+int (islower)(int c);
+int (isprint)(int c);
+int (ispunct)(int c);
+int (isspace)(int c);
+int (isupper)(int c);
+int (isxdigit)(int c);
+int (kill)(pid_t pid, int sig);
+long (labs)(long j);
+double (ldexp)(double x, int exp);
+ldiv_t (ldiv)(long numer, long denom);
+int (link)(const char *existing, const char *new);
+struct lconv *(localeconv)(void);
+struct tm *(localtime)(const time_t *timer);
+double (log)(double x);
+double (log10)(double x);
+void (longjmp)(jmp_buf env, int val);
+off_t (lseek)(int fildes, off_t offset, int whence);
+void *(malloc)(size_t size);
+int (mblen)(const char *s, size_t n);
+size_t (mbstowcs)(wchar_t *pwcs, const char *s, size_t n);
+int (mbtowc)(wchar_t *pwc, const char *s, size_t n);
+void *(memchr)(const void *s, int c, size_t n);
+int (memcmp)(const void *s1, const void *s2, size_t n);
+void *(memcpy)(void *s1, const void *s2, size_t n);
+void *(memmove)(void *s1, const void *s2, size_t n);
+void *(memset)(void *s, int c, size_t n);
+int (mkdir)(const char *path, mode_t mode);
+int (mkfifo)(const char *path, mode_t mode);
+time_t (mktime)(struct tm *timeptr);
+double (modf)(double value, double *iptr);
+int (open)(const char *path, int oflag, ...);
+DIR *(opendir)(const char *dirname);
+long (pathconf)(const char *path, int name);
+int (pause)(void);
+void (perror)(const char *s);
+int (pipe)(int fildes[2]);
+double (pow)(double x, double y);
+/* PRINTFLIKE1 */
+int (printf)(const char *format, ...);
+int (putc)(int c, FILE *stream);
+int (putchar)(int c);
+int (puts)(const char *s);
+void (qsort)(void *base, size_t nmemb, size_t size,
+ int (*compar)(const void *, const void *));
+int (raise)(int sig);
+int (rand)(void);
+ssize_t (read)(int fildes, void *buf, size_t nbyte);
+struct dirent *(readdir)(DIR *dirp);
+void *(realloc)(void *ptr, size_t size);
+int (remove)(const char *filename);
+int (rename)(const char *old, const char *new);
+void (rewind)(FILE *stream);
+void (rewinddir)(DIR *dirp);
+int (rmdir)(const char *path);
+/* SCANFLIKE1 */
+int (scanf)(const char *format, ...);
+void (setbuf)(FILE *stream, char *buf);
+int (setgid)(gid_t gid);
+int (setjmp)(jmp_buf env);
+char *(setlocale)(int category, const char *locale);
+int (setpgid)(pid_t pid, pid_t pgid);
+pid_t (setsid)(void);
+int (setuid)(uid_t uid);
+int (setvbuf)(FILE *stream, char *buf, int mode, size_t size);
+int (sigaction)(int sig, const struct sigaction *act,
+ struct sigaction *oact);
+int (sigaddset)(sigset_t *set, int signo);
+int (sigdelset)(sigset_t *set, int signo);
+int (sigemptyset)(sigset_t *set);
+int (sigfillset)(sigset_t *set);
+int (sigismember)(const sigset_t *set, int signo);
+void (siglongjmp)(sigjmp_buf env, int val);
+void (*(signal)(int sig, void (*func)(int)))(int);
+int (sigpending)(sigset_t *set);
+int (sigprocmask)(int how, const sigset_t *set, sigset_t *oset);
+int (sigsetjmp)(sigjmp_buf env, int savemask);
+int (sigsuspend)(const sigset_t *sigmask);
+double (sin)(double x);
+double (sinh)(double x);
+unsigned (sleep)(unsigned seconds);
+/* PRINTFLIKE2 */
+int (sprintf)(char *s, const char *format, ...);
+double (sqrt)(double x);
+void (srand)(unsigned seed);
+/* SCANFLIKE2 */
+int (sscanf)(const char *s, const char *format, ...);
+int (stat)(const char *path, struct stat *buf);
+char *(strcat)(char *s1, const char *s2);
+char *(strchr)(const char *s, int c);
+int (strcmp)(const char *s1, const char *s2);
+int (strcoll)(const char *s1, const char *s2);
+char *(strcpy)(char *s1, const char *s2);
+size_t (strcspn)(const char *s1, const char *s2);
+char *(strerror)(int errnum);
+size_t (strftime)(char *s, size_t maxsize, const char *format,
+ const struct tm *timeptr);
+size_t (strlen)(const char *s);
+char *(strncat)(char *s1, const char *s2, size_t n);
+int (strncmp)(const char *s1, const char *s2, size_t n);
+char *(strncpy)(char *s1, const char *s2, size_t n);
+char *(strpbrk)(const char *s1, const char *s2);
+char *(strrchr)(const char *s, int c);
+size_t (strspn)(const char *s1, const char *s2);
+char *(strstr)(const char *s1, const char *s2);
+double (strtod)(const char *nptr, char **endptr);
+char *(strtok)(char *s1, const char *s2);
+long (strtol)(const char *nptr, char **endptr, int base);
+unsigned long (strtoul)(const char *nptr, char **endptr, int base);
+size_t (strxfrm)(char *s1, const char *s2, size_t n);
+long (sysconf)(int name);
+int (system)(const char *string);
+double (tan)(double x);
+double (tanh)(double x);
+int (tcdrain)(int fildes);
+int (tcflow)(int fildes, int action);
+int (tcflush)(int fildes, int queue_selector);
+int (tcgetattr)(int fildes, struct termios *tp);
+pid_t (tcgetpgrp)(int fildes);
+int (tcsendbreak)(int fildes, int duration);
+int (tcsetattr)(int fildes, int options, const struct termios *tp);
+int (tcsetpgrp)(int fildes, pid_t pgrpid);
+time_t (time)(time_t *timer);
+clock_t (times)(struct tms *buffer);
+FILE *(tmpfile)(void);
+char *(tmpnam)(char *s);
+int (tolower)(int c);
+int (toupper)(int c);
+char *(ttyname)(int filedes);
+void (tzset)(void);
+mode_t (umask)(mode_t cmask);
+int (uname)(struct utsname *name);
+int (ungetc)(int c, FILE *stream);
+int (unlink)(const char *path);
+int (utime)(const char *path, const struct utimbuf *times);
+int (vfprintf)(FILE *stream, const char *format, va_list arg);
+int (vprintf)(const char *format, va_list arg);
+int (vsprintf)(char *s, const char *format, va_list arg);
+pid_t (wait)(int *statloc);
+pid_t (waitpid)(pid_t pid, int *stat_loc, int options);
+size_t (wcstombs)(char *s, const wchar_t *pwcs, size_t n);
+int (wctomb)(char *s, wchar_t wchar);
+ssize_t (write)(int fildes, const void *buf, size_t nbyte);
diff --git a/usr.bin/xlint/llib/llib-lstdc b/usr.bin/xlint/llib/llib-lstdc
new file mode 100644
index 0000000..f2b33da
--- /dev/null
+++ b/usr.bin/xlint/llib/llib-lstdc
@@ -0,0 +1,252 @@
+/* $NetBSD: llib-lstdc,v 1.3 1995/07/03 21:39:28 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+/* LINTLIBRARY */
+
+#define _ANSI_SOURCE
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <locale.h>
+#include <math.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+/* PROTOLIB1 */
+
+/*
+ * assert.h
+ */
+#ifdef __NetBSD__
+void (__assert)(const char *expression, int line, const char *file);
+#else
+void (assert)(int expression);
+#endif
+
+/*
+ * ctype.h
+ */
+int (isalnum)(int c);
+int (isalpha)(int c);
+int (iscntrl)(int c);
+int (isdigit)(int c);
+int (isgraph)(int c);
+int (islower)(int c);
+int (isprint)(int c);
+int (ispunct)(int c);
+int (isspace)(int c);
+int (isupper)(int c);
+int (isxdigit)(int c);
+int (tolower)(int c);
+int (toupper)(int c);
+
+/*
+ * errno.h
+ */
+int (errno);
+
+/*
+ * locale.h
+ */
+char *(setlocale)(int category, const char *locale);
+struct lconv *(localeconv)(void);
+
+/*
+ * math.h
+ */
+double (acos)(double x);
+double (asin)(double x);
+double (atan)(double x);
+double (atan2)(double y, double x);
+double (cos)(double x);
+double (sin)(double x);
+double (tan)(double x);
+double (cosh)(double x);
+double (sinh)(double x);
+double (tanh)(double x);
+double (exp)(double x);
+double (frexp)(double value, int *exp);
+double (ldexp)(double x, int exp);
+double (log)(double x);
+double (log10)(double x);
+double (modf)(double value, double *iptr);
+double (pow)(double x, double y);
+double (sqrt)(double x);
+double (ceil)(double x);
+double (fabs)(double x);
+double (floor)(double x);
+double (fmod)(double x, double y);
+
+/*
+ * setjmp.h
+ */
+int (setjmp)(jmp_buf env);
+void (longjmp)(jmp_buf env, int val);
+
+/*
+ * signal.h
+ */
+void (*(signal)(int sig, void (*func)(int)))(int);
+int (raise)(int sig);
+
+/*
+ * stdio.h
+ */
+int (remove)(const char *filename);
+int (rename)(const char *old, const char *new);
+FILE *(tmpfile)(void);
+char *(tmpnam)(char *s);
+int (fclose)(FILE *stream);
+int (fflush)(FILE *stream);
+FILE *(fopen)(const char *filename, const char *mode);
+FILE *(freopen)(const char *filename, const char *mode, FILE *stream);
+void (setbuf)(FILE *stream, char *buf);
+int (setvbuf)(FILE *stream, char *buf, int mode, size_t size);
+/* PRINTFLIKE2 */
+int (fprintf)(FILE *stream, const char *format, ...);
+/* SCANFLIKE2 */
+int (fscanf)(FILE *stream, const char *format, ...);
+/* PRINTFLIKE1 */
+int (printf)(const char *format, ...);
+/* SCANFLIKE1 */
+int (scanf)(const char *format, ...);
+/* PRINTFLIKE2 */
+int (sprintf)(char *s, const char *format, ...);
+/* SCANFLIKE2 */
+int (sscanf)(const char *s, const char *format, ...);
+int (vfprintf)(FILE *stream, const char *format, va_list arg);
+int (vprintf)(const char *format, va_list arg);
+int (vsprintf)(char *s, const char *format, va_list arg);
+int (fgetc)(FILE *stream);
+char *(fgets)(char *s, int n, FILE *stream);
+int (fputc)(int c, FILE *stream);
+int (fputs)(const char *s, FILE *stream);
+int (getc)(FILE *stream);
+int (getchar)(void);
+char *(gets)(char *s);
+int (putc)(int c, FILE *stream);
+int (putchar)(int c);
+int (puts)(const char *s);
+int (ungetc)(int c, FILE *stream);
+size_t (fread)(void *ptr, size_t size, size_t nmemb, FILE *stream);
+size_t (fwrite)(const void *ptr, size_t size, size_t nmemb, FILE *stream);
+int (fgetpos)(FILE *stream, fpos_t *pos);
+int (fseek)(FILE *stream, long offset, int whence);
+int (fsetpos)(FILE *stream, const fpos_t *pos);
+long (ftell)(FILE *stream);
+void (rewind)(FILE *stream);
+void (clearerr)(FILE *stream);
+int (feof)(FILE *stream);
+int (ferror)(FILE *stream);
+void (perror)(const char *s);
+
+/*
+ * stdlib.h
+ */
+double (atof)(const char *nptr);
+int (atoi)(const char *nptr);
+long (atol)(const char *nptr);
+double (strtod)(const char *nptr, char **endptr);
+long (strtol)(const char *nptr, char **endptr, int base);
+unsigned long (strtoul)(const char *nptr, char **endptr, int base);
+int (rand)(void);
+void (srand)(unsigned seed);
+void *(calloc)(size_t nmemb, size_t size);
+void (free)(void *ptr);
+void *(malloc)(size_t size);
+void *(realloc)(void *ptr, size_t size);
+void (abort)(void);
+int (atexit)(void (*func)(void));
+void (exit)(int status);
+char *(getenv)(const char *name);
+int (system)(const char *string);
+void *(bsearch)(const void *key, const void *base, size_t nmemb,
+ size_t size, int (*compar)(const void *, const void *));
+void (qsort)(void *base, size_t nmemb, size_t size,
+ int (*compar)(const void *, const void *));
+int (abs)(int j);
+div_t (div)(int numer, int denom);
+long (labs)(long j);
+ldiv_t (ldiv)(long numer, long denom);
+int (mblen)(const char *s, size_t n);
+int (mbtowc)(wchar_t *PWC, const char *s, size_t n);
+int (wctomb)(char *s, wchar_t wchar);
+size_t (mbstowcs)(wchar_t *pwcs, const char *s, size_t n);
+size_t (wcstombs)(char *s, const wchar_t *pwcs, size_t n);
+
+/*
+ * string.h
+ */
+void *(memcpy)(void *s1, const void *s2, size_t n);
+void *(memmove)(void *s1, const void *s2, size_t n);
+char *(strcpy)(char *s1, const char *s2);
+char *(strncpy)(char *s1, const char *s2, size_t n);
+char *(strcat)(char *s1, const char *s2);
+char *(strncat)(char *s1, const char *s2, size_t n);
+int (memcmp)(const void *s1, const void *s2, size_t n);
+int (strcmp)(const char *s1, const char *s2);
+int (strcoll)(const char *s1, const char *s2);
+int (strncmp)(const char *s1, const char *s2, size_t n);
+size_t (strxfrm)(char *s1, const char *s2, size_t n);
+void *(memchr)(const void *s, int c, size_t n);
+char *(strchr)(const char *s, int c);
+size_t (strcspn)(const char *s1, const char *s2);
+char *(strpbrk)(const char *s1, const char *s2);
+char *(strrchr)(const char *s1, int c);
+size_t (strspn)(const char *s1, const char *s2);
+char *(strstr)(const char *s1, const char *s2);
+char *(strtok)(char *s1, const char *s2);
+void *(memset)(void *s, int c, size_t n);
+char *(strerror)(int errnom);
+size_t (strlen)(const char *s);
+
+/*
+ * time.h
+ */
+clock_t (clock)(void);
+double (difftime)(time_t time1, time_t time2);
+time_t (mktime)(struct tm *timeptr);
+time_t (time)(time_t *timer);
+char *(asctime)(const struct tm *timeptr);
+char *(ctime)(const time_t *timer);
+struct tm *(gmtime)(const time_t *timer);
+struct tm *(localtime)(const time_t *timer);
+size_t (strftime)(char *s, size_t maxsize, const char *format,
+ const struct tm *timeptr);
diff --git a/usr.bin/xlint/xlint/Makefile b/usr.bin/xlint/xlint/Makefile
new file mode 100644
index 0000000..35fea3f
--- /dev/null
+++ b/usr.bin/xlint/xlint/Makefile
@@ -0,0 +1,17 @@
+# $NetBSD: Makefile,v 1.2 1995/07/03 21:25:14 cgd Exp $
+
+.PATH: ${.CURDIR}/../lint1
+
+PROG= xlint
+SRCS= xlint.c mem.c
+MAN1= lint.1
+
+CFLAGS+=-I${.CURDIR}/../lint1
+
+realinstall:
+ ${INSTALL} ${COPY} ${STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${PROG} ${DESTDIR}${BINDIR}/lint
+
+
+.include "${.CURDIR}/../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.bin/xlint/xlint/lint.1 b/usr.bin/xlint/xlint/lint.1
new file mode 100644
index 0000000..a020a53
--- /dev/null
+++ b/usr.bin/xlint/xlint/lint.1
@@ -0,0 +1,509 @@
+.\" $NetBSD: lint.1,v 1.3 1995/10/23 13:45:31 jpo Exp $
+.\"
+.\" Copyright (c) 1994, 1995 Jochen Pohl
+.\" All Rights Reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Jochen Pohl for
+.\" The NetBSD Project.
+.\" 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.
+.\"
+.Dd August 28, 1994
+.Dt LINT 1
+.Os FreeBSD
+.Sh NAME
+.Nm lint
+.Nd a C program verifier.
+.Sh SYNOPSIS
+.Nm lint
+.Op Fl abceghprvxzHFV
+.Op Fl s Ns | Ns Fl t
+.Op Fl i Ns | Ns Fl nu
+.Op Fl D Ns Ar name Ns Op =def
+.Op Fl U Ns Ar name
+.Op Fl I Ns Ar directory
+.Op Fl L Ns Ar directory
+.Op Fl l Ns Ar library
+.Op Fl o Ns Ar outputfile
+.Ar
+.Nm lint
+.Op Fl abceghprvzHFV
+.Op Fl s Ns | Ns Fl t
+.Fl C Ns Ar library
+.Op Fl D Ns Ar name Ns Op =def
+.Op Fl I Ns Ar directory
+.Op Fl U Ns Ar name
+.Ar
+.Sh DESCRIPTION
+.Nm
+attempts to detect features of the named C program files
+that are likely to be bugs, to be non-portable, or to be
+wasteful. It also performs stricter type checking then does
+the C compiler.
+.Nm
+runs the C preprocessor as its first phase, with the
+preprocessor symbol
+.Sy lint
+defined to allow certain questionable code to be altered
+or skipped by
+.Nm lint .
+Therefore, this symbol should be thought of as a reserved
+word for all code that is to be checked by
+.Nm lint .
+.Pp
+Among the possible problems that are currently noted are
+unreachable statements, loops not entered at the top,
+variables declared and not used, and logical expressions
+with constant values. Function calls are checked for
+inconsistencies, such as calls to functions that return
+values in some places and not in others, functions called
+with varying numbers of arguments, function calls that
+pass arguments of a type other than the type the function
+expects to receive, functions whose values are not used,
+and calls to functions not returning values that use
+the non-existent return value of the function.
+.Pp
+Filename arguments ending with
+.Pa \&.c
+are taken to be C source files. Filename arguments with
+names ending with
+.Pa \&.ln
+are taken to be the result of an earlier invocation of
+.Nm lint ,
+with either the
+.Fl i ,
+.Fl o
+or
+.Fl C
+option in effect. The
+.Pa \&.ln
+files are analogous to the
+.Pa \&.o
+(object) files produced by
+.Xr cc 1
+from
+.Pa \&.c
+files.
+.Nm
+also accepts special libraries specified with the
+.Fl l
+option, which contain definitions of library routines and
+variables.
+.Pp
+.Nm
+takes all the
+.Pa \&.c , \&.ln ,
+and
+.Pa llib-l Ns Ar library Ns Pa \&.ln
+(lint library) files and processes them in command-line order.
+By default,
+.Nm
+appends the standard C lint library
+.Pq Pa llib-lc.ln
+to the end of the list of files. When the
+.Fl i
+option is used, the
+.Pa \&.ln
+files are ignored.
+Also, when the
+.Fl o
+or
+.Fl i
+options are used, the
+.Pa llib-l Ns Ar library Ns Pa \&.ln
+files are ignored. When the
+.Fl i
+option is
+.Em omitted
+the second pass of
+.Nm
+checks this list of files for mutual compatibility. At this point,
+if a complaint stems not from a given source file, but from one of
+its included files, the source filename will be printed followed by
+a question mark.
+.Pp
+.Sy Options
+.Bl -tag -width Fl
+.It Fl a
+Report assignments of
+.Sy long
+values to variables that are not
+.Sy long .
+.It Fl aa
+Additional to
+.Fl a ,
+report
+.Em all
+assignments of integer values to other integer values which
+cause implicit narrowing conversion.
+.It Fl b
+Report
+.Sy break
+statements that cannot be reached. This is not the default
+because, unfortunately, most
+.Xr lex 1
+and many
+.Xr yacc 1
+outputs produce many such complaints.
+.It Fl c
+Complain about casts which have questionable portability.
+.It Fl e
+Complain about unusual operations on
+.Sy enum Ns -Types
+and combinations of
+.Sy enum Ns -
+and
+.Sy integer Ns -Types.
+.It Fl g
+Don't print warnings for some extensions of
+.Xr gcc 1
+to the C language. Currently these are nonconstant initializers in
+automatic aggregate initializations, arithmetic on pointer to void,
+zero sized structures, subscripting of non-lvalue arrays, prototypes
+overriding old style function declarations and long long
+integer types. The
+.Fl g
+flag also turns on the keywords
+.Sy asm
+and
+.Sy inline
+(alternate keywords with leading underscores for both
+.Sy asm
+and
+.Sy inline
+are always available).
+.It Fl h
+Apply a number of heuristic tests to attempt to intuit
+bugs, improve style, and reduce waste.
+.It Fl i
+Produce a
+.Pa \&.ln
+file for every
+.Pa \&.c
+file on the command line. These
+.Pa \&.ln
+files are the product of
+.Nm lint Ns 's
+first pass only, and are not checked for compatibility
+between functions.
+.It Fl n
+Do not check compatibility against the standard library.
+.It Fl p
+Attempt to check portability of code to other dialects of C.
+.It Fl r
+In case of redeclarations report the position of the
+previous declaration.
+.It Fl s
+Strict ANSI C mode. Issue warnings and errors required by ANSI C.
+Also do not produce warnings for constructs which behave
+differently in traditional C and ANSI C. With the
+.Fl s
+flag,
+.Li __STRICT_ANSI__
+is a predefined preprocessor macro.
+.It Fl t
+Traditional C mode.
+.Li __STDC__
+is not predefined in this mode. Warnings are printed for constructs
+not allowed in traditional C. Warnings for constructs which behave
+differently in traditional C and ANSI C are suppressed. Preprocessor
+macros describing the machine type (e.g.
+.Li sun3 Ns )
+and machine architecture (e.g.
+.Li m68k Ns )
+are defined without leading and trailing underscores. The keywords
+.Sy const Ns ,
+.Sy volatile
+and
+.Sy signed
+are not available in traditional C mode (although the alternate
+keywords with leading underscores still are).
+.It Fl u
+Do not complain about functions and external variables used
+and not defined, or defined and not used (this is suitable
+for running
+.Nm
+on a subset of files comprising part of a larger program).
+.It Fl v
+Suppress complaints about unused arguments in functions.
+.It Fl x
+Report variables referred to by
+.Sy extern
+declarations, but never used.
+.It Fl z
+Do not complain about structures that are never defined
+(for example, using a structure pointer without knowing
+its contents).
+.It Fl C Ns Ar library
+Create a
+.Nm
+library with the name
+.Pa llib-l Ns Ar library Ns Pa .ln .
+This library is built from all
+.Pa \&.c
+and
+.Pa \&.ln
+input files. After all global definitions of functions and
+variables in these files are written to the newly created library,
+.Nm
+checks all input files, including libraries specified with the
+.Fl l
+option, for mutual compatibility.
+.It Fl D Ns Ar name Ns Op =def
+Define
+.Ar name
+for
+.Xr cpp 1 ,
+as if by a
+.Li #define
+directive. If no definition is given,
+.Ar name
+is defined as 1.
+.It Fl I Ns Ar directory
+Add
+.Ar directory
+to the list of directories in which to search for include files.
+.It Fl l Ns Ar library
+Include the lint library
+.Pa llib-l Ns Ar library Ns Pa \&.ln .
+.It Fl L Ns Ar directory
+Search for lint libraries in
+.Ar directory
+and
+.Ar directory Ns Pa /lint
+before searching the standard place.
+.It Fl F
+Print pathnames of files.
+.Nm
+normally prints the filename without the path.
+.It Fl H
+If a complaint stems from an included file
+.Nm
+prints the name of the included file instead of the source file name
+followed by a question mark.
+.It Fl o Ns Ar outputfile
+Name the output file
+.Ar outputfile .
+The output file produced is the input that is given to
+.Nm lint Ns 's
+second pass. The
+.Fl o
+option simply saves this file in the named output file. If the
+.Fl i
+option is also used the files are not checked for compatibility.
+To produce a
+.Pa llib-l Ns Ar library Ns Pa \&.ln
+without extraneous messages, use of the
+.Fl u
+option is suggested. The
+.Fl v
+option is useful if the source file(s) for the lint library
+are just external interfaces.
+.It Fl U Ns Ar name
+Remove any initial definition of
+.Ar name
+for the preprocessor.
+.It Fl V
+Print the command lines constructed by the controller program to
+run the C preprocessor and
+.Nm lint Ns 's
+first and second pass.
+.El
+.Pp
+.Sy Input Grammar
+.Pp
+.Nm lint Ns 's
+first pass reads standard C source files.
+.Nm
+recognizes the following C comments as commands.
+.Bl -tag -width Fl
+.It Li /* ARGSUSED Ns Ar n Li */
+makes
+.Nm
+check only the first
+.Ar n
+arguments for usage; a missing
+.Ar n
+is taken to be 0 (this option acts like the
+.Fl v
+option for the next function).
+.It Li /* CONSTCOND */ No or Xo
+.Li /* CONSTANTCOND */ No or
+.Li /* CONSTANTCONDITION */
+.Xc
+suppress complaints about constant operands for the next expression.
+.It Li /*\ FALLTHRU\ */ No or Xo
+.Li /* FALLTHROUGH */
+.Xc
+suppress complaints about fall through to a
+.Sy case
+or
+.Sy default
+labelled statement. This directive should be placed immediately
+preceding the label.
+.It Li /* LINTLIBRARY */
+At the beginning of a file, mark all functions and variables defined
+in this file as
+.Em used .
+Also shut off complaints about unused function arguments.
+.It Li /* LINTED Xo
+.Op Ar comment
+.Li */ No or
+.Li /* NOSTRICT
+.Op Ar comment
+.Li */
+.Xc
+Suppresses any intra-file warning except those dealing with
+unused variables or functions. This directive should be placed
+on the line immediately preceding where the lint warning occurred.
+.It Li /* LONGLONG */
+Suppress complaints about use of long long integer types.
+.It Li /* NOTREACHED */
+At appropriate points, inhibit complaints about unreachable code.
+(This comment is typically placed just after calls to functions
+like
+.Xr exit 3 ).
+.It Li /* PRINTFLIKE Ns Ar n Li */
+makes
+.Nm
+check the first
+.Pq Ar n Ns No -1
+arguments as usual. The
+.Ar n Ns No -th
+argument is interpreted as a
+.Sy printf
+format string that is used to check the remaining arguments.
+.It Li /* PROTOLIB Ns Ar n Li */
+causes
+.Nm
+to treat function declaration prototypes as function definitions
+if
+.Ar n
+is non-zero. This directive can only be used in conjunction with
+the
+.Li /* LINTLIBRARY */
+directive. If
+.Ar n
+is zero, function prototypes will be treated normally.
+.It Li /* SCANFLIKE Ns Ar n Li */
+makes
+.Nm
+check the first
+.Pq Ar n Ns No -1
+arguments as usual. The
+.Ar n Ns No -th
+argument is interpreted as a
+.Sy scanf
+format string that is used to check the remaining arguments.
+.It Li /* VARARGS Ns Ar n Li */
+Suppress the usual checking for variable numbers of arguments in
+the following function declaration. The data types of the first
+.Ar n
+arguments are checked; a missing
+.Ar n
+is taken to be 0.
+.El
+.Pp
+The behavior of the
+.Fl i
+and the
+.Fl o
+options allows for incremental use of
+.Nm
+on a set of C source files. Generally, one invokes
+.Nm
+once for each source file with the
+.Fl i
+option. Each of these invocations produces a
+.Pa \&.ln
+file that corresponds to the
+.Pa \&.c
+file, and prints all messages that are about just that
+source file. After all the source files have been separately
+run through
+.Nm lint ,
+it is invoked once more (without the
+.Fl i
+option), listing all the
+.Pa \&.ln
+files with the needed
+.Fl l Ns Ar library
+options. this will print all the inter-file inconsistencies. This
+scheme works well with
+.Xr make 1 ;
+it allows
+.Xr make 1
+to be used to
+.Nm
+only the source files that have been modified since the last
+time the set of source files were
+.Nm lint Ns No ed .
+.Sh ENVIRONMENT
+.Bl -tag -width Fl
+.It Ev LIBDIR
+the directory where the lint libraries specified by the
+.Fl l Ns Ar library
+option must exist. If this environment variable is undefined,
+then the default path
+.Pa /usr/libdata/lint
+will be used to search for the libraries.
+.It Ev TMPDIR
+usually the path for temporary files can be redefined by setting
+this environment variable.
+.El
+.Sh FILES
+.Bl -tag -width /usr/libdata/lint/llib-lc.ln -compact
+.It Pa /usr/libexec/lint Ns Bq 12
+programs
+.It Pa /usr/libdata/lint/llib-l*.ln
+various prebuilt lint libraries
+.It Pa /tmp/lint*
+temporaries
+.Sh SEE ALSO
+.Xr cc 1 ,
+.Xr cpp 1 ,
+.Xr make 1
+.Sh AUTHORS
+Jochen Pohl
+.Sh BUGS
+The routines
+.Xr exit 3 ,
+.Xr longjmp 3
+and other functions that do not return are not understood; this
+causes various incorrect diagnostics.
+.Pp
+Static functions which are used only before their first
+extern declaration are reported as unused.
+.Pp
+Libraries created by the
+.Fl o
+option will, when used in later
+.Nm
+runs, cause certain errors that were reported when the libraries
+were created to be reported again, and cause line numbers and file
+names from the original source used to create those libraries
+to be reported in error messages. For these reasons, it is recommended
+to use the
+.Fl C
+option to create lint libraries.
diff --git a/usr.bin/xlint/xlint/pathnames.h b/usr.bin/xlint/xlint/pathnames.h
new file mode 100644
index 0000000..d03845d
--- /dev/null
+++ b/usr.bin/xlint/xlint/pathnames.h
@@ -0,0 +1,38 @@
+/* $NetBSD: pathnames.h,v 1.2 1995/07/03 21:25:20 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+/* directory where lint1 and lint2 reside */
+#define PATH_LIBEXEC "/usr/libexec"
+
+/* default library search path */
+#define PATH_LINTLIB "/usr/libdata/lint"
diff --git a/usr.bin/xlint/xlint/xlint.c b/usr.bin/xlint/xlint/xlint.c
new file mode 100644
index 0000000..e4ad124
--- /dev/null
+++ b/usr.bin/xlint/xlint/xlint.c
@@ -0,0 +1,771 @@
+/* $NetBSD: xlint.c,v 1.3 1995/10/23 14:29:30 jpo Exp $ */
+
+/*
+ * Copyright (c) 1994, 1995 Jochen Pohl
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jochen Pohl for
+ * The NetBSD Project.
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "$NetBSD: xlint.c,v 1.3 1995/10/23 14:29:30 jpo Exp $";
+#endif
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+#include <paths.h>
+
+#include "lint.h"
+#include "pathnames.h"
+
+/* directory for temporary files */
+static const char *tmpdir;
+
+/* path name for cpp output */
+static char *cppout;
+
+/* files created by 1st pass */
+static char **p1out;
+
+/* input files for 2nd pass (without libraries) */
+static char **p2in;
+
+/* library which will be created by 2nd pass */
+static char *p2out;
+
+/* flags always passed to cpp */
+static char **cppflags;
+
+/* flags for cpp, controled by sflag/tflag */
+static char **lcppflgs;
+
+/* flags for lint1 */
+static char **l1flags;
+
+/* flags for lint2 */
+static char **l2flags;
+
+/* libraries for lint2 */
+static char **l2libs;
+
+/* default libraries */
+static char **deflibs;
+
+/* additional libraries */
+static char **libs;
+
+/* search path for libraries */
+static char **libsrchpath;
+
+/* flags */
+static int iflag, oflag, Cflag, sflag, tflag, Fflag;
+
+/* print the commands executed to run the stages of compilation */
+static int Vflag;
+
+/* filename for oflag */
+static char *outputfn;
+
+/* reset after first .c source has been processed */
+static int first = 1;
+
+/*
+ * name of a file which is currently written by a child and should
+ * be removed after abnormal termination of the child
+ */
+static const char *currfn;
+
+
+static void appstrg __P((char ***, char *));
+static void appcstrg __P((char ***, const char *));
+static void applst __P((char ***, char *const *));
+static void freelst __P((char ***));
+static char *concat2 __P((const char *, const char *));
+static char *concat3 __P((const char *, const char *, const char *));
+static void terminate __P((int));
+static const char *basename __P((const char *, int));
+static void appdef __P((char ***, const char *));
+static void usage __P((void));
+static void fname __P((const char *, int));
+static void runchild __P((const char *, char *const *, const char *));
+static void findlibs __P((char *const *));
+static int rdok __P((const char *));
+static void lint2 __P((void));
+static void cat __P((char *const *, const char *));
+
+/*
+ * Some functions to deal with lists of strings.
+ * Take care that we get no surprises in case of asyncron signals.
+ */
+static void
+appstrg(lstp, s)
+ char ***lstp, *s;
+{
+ char **lst, **olst;
+ int i;
+
+ olst = *lstp;
+ for (i = 0; olst[i] != NULL; i++) ;
+ lst = xmalloc((i + 2) * sizeof (char *));
+ (void)memcpy(lst, olst, i * sizeof (char *));
+ lst[i] = s;
+ lst[i + 1] = NULL;
+ *lstp = lst;
+}
+
+static void
+appcstrg(lstp, s)
+ char ***lstp;
+ const char *s;
+{
+ appstrg(lstp, xstrdup(s));
+}
+
+static void
+applst(destp, src)
+ char ***destp;
+ char *const *src;
+{
+ int i, k;
+ char **dest, **odest;
+
+ odest = *destp;
+ for (i = 0; odest[i] != NULL; i++) ;
+ for (k = 0; src[k] != NULL; k++) ;
+ dest = xmalloc((i + k + 1) * sizeof (char *));
+ (void)memcpy(dest, odest, i * sizeof (char *));
+ for (k = 0; src[k] != NULL; k++)
+ dest[i + k] = xstrdup(src[k]);
+ dest[i + k] = NULL;
+ *destp = dest;
+ free(odest);
+}
+
+static void
+freelst(lstp)
+ char ***lstp;
+{
+ char *s;
+ int i;
+
+ for (i = 0; (*lstp)[i] != NULL; i++) ;
+ while (i-- > 0) {
+ s = (*lstp)[i];
+ (*lstp)[i] = NULL;
+ free(s);
+ }
+}
+
+static char *
+concat2(s1, s2)
+ const char *s1, *s2;
+{
+ char *s;
+
+ s = xmalloc(strlen(s1) + strlen(s2) + 1);
+ (void)strcpy(s, s1);
+ (void)strcat(s, s2);
+
+ return (s);
+}
+
+static char *
+concat3(s1, s2, s3)
+ const char *s1, *s2, *s3;
+{
+ char *s;
+
+ s = xmalloc(strlen(s1) + strlen(s2) + strlen(s3) + 1);
+ (void)strcpy(s, s1);
+ (void)strcat(s, s2);
+ (void)strcat(s, s3);
+
+ return (s);
+}
+
+/*
+ * Clean up after a signal.
+ */
+static void
+terminate(signo)
+ int signo;
+{
+ int i;
+
+ if (cppout != NULL)
+ (void)remove(cppout);
+
+ if (p1out != NULL) {
+ for (i = 0; p1out[i] != NULL; i++)
+ (void)remove(p1out[i]);
+ }
+
+ if (p2out != NULL)
+ (void)remove(p2out);
+
+ if (currfn != NULL)
+ (void)remove(currfn);
+
+ exit(signo != 0 ? 1 : 0);
+}
+
+/*
+ * Returns a pointer to the last component of strg after delim.
+ * Returns strg if the string does not contain delim.
+ */
+static const char *
+basename(strg, delim)
+ const char *strg;
+ int delim;
+{
+ const char *cp, *cp1, *cp2;
+
+ cp = cp1 = cp2 = strg;
+ while (*cp != '\0') {
+ if (*cp++ == delim) {
+ cp2 = cp1;
+ cp1 = cp;
+ }
+ }
+ return (*cp1 == '\0' ? cp2 : cp1);
+}
+
+static void
+appdef(lstp, def)
+ char ***lstp;
+ const char *def;
+{
+ appstrg(lstp, concat2("-D__", def));
+ appstrg(lstp, concat3("-D__", def, "__"));
+}
+
+static void
+usage()
+{
+ (void)printf("lint [-abceghprvxzHF] [-s|-t] [-i|-nu] [-Dname[=def]] [-Uname]\n");
+ (void)printf(" [-Idirectory] [-Ldirectory] [-llibrary] [-ooutputfile] file ...\n");
+ (void)printf("\n");
+ (void)printf("lint [-abceghprvzHF] [-s|-t] -Clibrary [-Dname[=def]]\n");
+ (void)printf(" [-Idirectory] [-Uname] file ...\n");
+ terminate(-1);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ char flgbuf[3], *tmp, *s;
+ size_t len;
+ struct utsname un;
+
+ if ((tmp = getenv("TMPDIR")) == NULL || (len = strlen(tmp)) == 0) {
+ tmpdir = xstrdup(_PATH_TMP);
+ } else {
+ s = xmalloc(len + 2);
+ (void)sprintf(s, "%s%s", tmp, tmp[len - 1] == '/' ? "" : "/");
+ tmpdir = s;
+ }
+
+ cppout = xmalloc(strlen(tmpdir) + sizeof ("lint0.XXXXXX"));
+ (void)sprintf(cppout, "%slint0.XXXXXX", tmpdir);
+ if (mktemp(cppout) == NULL) {
+ warn("can't make temp");
+ terminate(-1);
+ }
+
+ p1out = xcalloc(1, sizeof (char *));
+ p2in = xcalloc(1, sizeof (char *));
+ cppflags = xcalloc(1, sizeof (char *));
+ lcppflgs = xcalloc(1, sizeof (char *));
+ l1flags = xcalloc(1, sizeof (char *));
+ l2flags = xcalloc(1, sizeof (char *));
+ l2libs = xcalloc(1, sizeof (char *));
+ deflibs = xcalloc(1, sizeof (char *));
+ libs = xcalloc(1, sizeof (char *));
+ libsrchpath = xcalloc(1, sizeof (char *));
+
+ appcstrg(&cppflags, "-lang-c");
+ appcstrg(&cppflags, "-undef");
+ appcstrg(&cppflags, "-$");
+ appcstrg(&cppflags, "-C");
+ appcstrg(&cppflags, "-Wcomment");
+#if defined (__FreeBSD__) && (__FreeBSD__ == 3)
+ appcstrg(&cppflags, "-D__FreeBSD__=3");
+#elif defined (__FreeBSD__) && (__FreeBSD__ == 2)
+ appcstrg(&cppflags, "-D__FreeBSD__=2");
+#else
+# error "This ain't NetBSD. You lose!"
+ appcstrg(&cppflags, "-D__NetBSD__");
+#endif
+ appcstrg(&cppflags, "-Dlint"); /* XXX don't def. with -s */
+ appdef(&cppflags, "lint");
+ appdef(&cppflags, "unix");
+
+ appcstrg(&lcppflgs, "-Wtraditional");
+
+ if (uname(&un) == -1)
+ err(1, "uname");
+ appdef(&cppflags, un.machine);
+ appstrg(&lcppflgs, concat2("-D", un.machine));
+
+#ifdef MACHINE_ARCH
+ if (strcmp(un.machine, MACHINE_ARCH) != 0) {
+ appdef(&cppflags, MACHINE_ARCH);
+ appstrg(&lcppflgs, concat2("-D", MACHINE_ARCH));
+ }
+#endif
+
+ appcstrg(&deflibs, "c");
+
+ if (signal(SIGHUP, terminate) == SIG_IGN)
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, terminate);
+ (void)signal(SIGQUIT, terminate);
+ (void)signal(SIGTERM, terminate);
+
+ while (argc > optind) {
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ c = getopt(argc, argv, "abceghil:no:prstuvxzC:D:FHI:L:U:V");
+
+ switch (c) {
+
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'e':
+ case 'g':
+ case 'r':
+ case 'v':
+ case 'z':
+ (void)sprintf(flgbuf, "-%c", c);
+ appcstrg(&l1flags, flgbuf);
+ break;
+
+ case 'F':
+ Fflag = 1;
+ /* FALLTHROUGH */
+ case 'u':
+ case 'h':
+ (void)sprintf(flgbuf, "-%c", c);
+ appcstrg(&l1flags, flgbuf);
+ appcstrg(&l2flags, flgbuf);
+ break;
+
+ case 'i':
+ if (Cflag)
+ usage();
+ iflag = 1;
+ break;
+
+ case 'n':
+ freelst(&deflibs);
+ break;
+
+ case 'p':
+ appcstrg(&l1flags, "-p");
+ appcstrg(&l2flags, "-p");
+ if (*deflibs != NULL) {
+ freelst(&deflibs);
+ appcstrg(&deflibs, "c");
+ }
+ break;
+
+ case 's':
+ if (tflag)
+ usage();
+ freelst(&lcppflgs);
+ appcstrg(&lcppflgs, "-trigraphs");
+ appcstrg(&lcppflgs, "-Wtrigraphs");
+ appcstrg(&lcppflgs, "-pedantic");
+ appcstrg(&lcppflgs, "-D__STRICT_ANSI__");
+ appcstrg(&l1flags, "-s");
+ appcstrg(&l2flags, "-s");
+ sflag = 1;
+ break;
+
+ case 't':
+ if (sflag)
+ usage();
+ freelst(&lcppflgs);
+ appcstrg(&lcppflgs, "-traditional");
+ appstrg(&lcppflgs, concat2("-D", MACHINE));
+#ifdef MACHINE_ARCH
+ appstrg(&lcppflgs, concat2("-D", MACHINE_ARCH));
+#endif
+ appcstrg(&l1flags, "-t");
+ appcstrg(&l2flags, "-t");
+ tflag = 1;
+ break;
+
+ case 'x':
+ appcstrg(&l2flags, "-x");
+ break;
+
+ case 'C':
+ if (Cflag || oflag || iflag)
+ usage();
+ Cflag = 1;
+ appstrg(&l2flags, concat2("-C", optarg));
+ p2out = xmalloc(sizeof ("llib-l.ln") + strlen(optarg));
+ (void)sprintf(p2out, "llib-l%s.ln", optarg);
+ freelst(&deflibs);
+ break;
+
+ case 'D':
+ case 'I':
+ case 'U':
+ (void)sprintf(flgbuf, "-%c", c);
+ appstrg(&cppflags, concat2(flgbuf, optarg));
+ break;
+
+ case 'l':
+ appcstrg(&libs, optarg);
+ break;
+
+ case 'o':
+ if (Cflag || oflag)
+ usage();
+ oflag = 1;
+ outputfn = xstrdup(optarg);
+ break;
+
+ case 'L':
+ appcstrg(&libsrchpath, optarg);
+ break;
+
+ case 'H':
+ appcstrg(&l2flags, "-H");
+ break;
+
+ case 'V':
+ Vflag = 1;
+ break;
+
+ case '?':
+ usage();
+ /* NOTREACHED */
+
+ case -1:
+ /* filename */
+ fname(argv[0], argc == 1);
+ first = 0;
+ optind = 1;
+ }
+
+ }
+
+ if (first)
+ usage();
+
+ if (iflag)
+ terminate(0);
+
+ if (!oflag) {
+ if ((s = getenv("LIBDIR")) == NULL || strlen(s) == 0)
+ s = PATH_LINTLIB;
+ appcstrg(&libsrchpath, s);
+ findlibs(libs);
+ findlibs(deflibs);
+ }
+
+ (void)printf("Lint pass2:\n");
+ lint2();
+
+ if (oflag)
+ cat(p2in, outputfn);
+
+ if (Cflag)
+ p2out = NULL;
+
+ terminate(0);
+ /* NOTREACHED */
+ return 0;
+}
+
+/*
+ * Read a file name from the command line
+ * and pass it through lint1 if it is a C source.
+ */
+static void
+fname(name, last)
+ const char *name;
+ int last;
+{
+ const char *bn, *suff;
+ char **args, *ofn, *path;
+ size_t len;
+
+ bn = basename(name, '/');
+ suff = basename(bn, '.');
+
+ if (strcmp(suff, "ln") == 0) {
+ /* only for lint2 */
+ if (!iflag)
+ appcstrg(&p2in, name);
+ return;
+ }
+
+ if (strcmp(suff, "c") != 0 &&
+ (strncmp(bn, "llib-l", 6) != 0 || bn != suff)) {
+ warnx("unknown file type: %s\n", name);
+ return;
+ }
+
+ if (!iflag || !first || !last)
+ (void)printf("%s:\n", Fflag ? name : bn);
+
+ /* build the name of the output file of lint1 */
+ if (oflag) {
+ ofn = outputfn;
+ outputfn = NULL;
+ oflag = 0;
+ } else if (iflag) {
+ ofn = xmalloc(strlen(bn) + (bn == suff ? 4 : 2));
+ len = bn == suff ? strlen(bn) : (suff - 1) - bn;
+ (void)sprintf(ofn, "%.*s", (int)len, bn);
+ (void)strcat(ofn, ".ln");
+ } else {
+ ofn = xmalloc(strlen(tmpdir) + sizeof ("lint1.XXXXXX"));
+ (void)sprintf(ofn, "%slint1.XXXXXX", tmpdir);
+ if (mktemp(ofn) == NULL) {
+ warn("can't make temp");
+ terminate(-1);
+ }
+ }
+ if (!iflag)
+ appcstrg(&p1out, ofn);
+
+ args = xcalloc(1, sizeof (char *));
+
+ /* run cpp */
+
+ path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/cpp"));
+ (void)sprintf(path, "%s/cpp", PATH_LIBEXEC);
+
+ appcstrg(&args, path);
+ applst(&args, cppflags);
+ applst(&args, lcppflgs);
+ appcstrg(&args, name);
+ appcstrg(&args, cppout);
+
+ runchild(path, args, cppout);
+ free(path);
+ freelst(&args);
+
+ /* run lint1 */
+
+ path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint1"));
+ (void)sprintf(path, "%s/lint1", PATH_LIBEXEC);
+
+ appcstrg(&args, path);
+ applst(&args, l1flags);
+ appcstrg(&args, cppout);
+ appcstrg(&args, ofn);
+
+ runchild(path, args, ofn);
+ free(path);
+ freelst(&args);
+
+ appcstrg(&p2in, ofn);
+ free(ofn);
+
+ free(args);
+}
+
+static void
+runchild(path, args, crfn)
+ const char *path, *crfn;
+ char *const *args;
+{
+ int status, rv, signo, i;
+
+ if (Vflag) {
+ for (i = 0; args[i] != NULL; i++)
+ (void)printf("%s ", args[i]);
+ (void)printf("\n");
+ }
+
+ currfn = crfn;
+
+ (void)fflush(stdout);
+
+ switch (fork()) {
+ case -1:
+ warn("cannot fork");
+ terminate(-1);
+ /* NOTREACHED */
+ default:
+ /* parent */
+ break;
+ case 0:
+ /* child */
+ (void)execv(path, args);
+ warn("cannot exec %s", path);
+ exit(1);
+ /* NOTREACHED */
+ }
+
+ while ((rv = wait(&status)) == -1 && errno == EINTR) ;
+ if (rv == -1) {
+ warn("wait");
+ terminate(-1);
+ }
+ if (WIFSIGNALED(status)) {
+ signo = WTERMSIG(status);
+ warnx("%s got SIG%s", path, sys_signame[signo]);
+ terminate(-1);
+ }
+ if (WEXITSTATUS(status) != 0)
+ terminate(-1);
+ currfn = NULL;
+}
+
+static void
+findlibs(liblst)
+ char *const *liblst;
+{
+ int i, k;
+ const char *lib, *path;
+ char *lfn;
+ size_t len;
+
+ lfn = NULL;
+
+ for (i = 0; (lib = liblst[i]) != NULL; i++) {
+ for (k = 0; (path = libsrchpath[k]) != NULL; k++) {
+ len = strlen(path) + strlen(lib);
+ lfn = xrealloc(lfn, len + sizeof ("/llib-l.ln"));
+ (void)sprintf(lfn, "%s/llib-l%s.ln", path, lib);
+ if (rdok(lfn))
+ break;
+ lfn = xrealloc(lfn, len + sizeof ("/lint/llib-l.ln"));
+ (void)sprintf(lfn, "%s/lint/llib-l%s.ln", path, lib);
+ if (rdok(lfn))
+ break;
+ }
+ if (path != NULL) {
+ appstrg(&l2libs, concat2("-l", lfn));
+ } else {
+ warnx("cannot find llib-l%s.ln", lib);
+ }
+ }
+
+ free(lfn);
+}
+
+static int
+rdok(path)
+ const char *path;
+{
+ struct stat sbuf;
+
+ if (stat(path, &sbuf) == -1)
+ return (0);
+ if ((sbuf.st_mode & S_IFMT) != S_IFREG)
+ return (0);
+ if (access(path, R_OK) == -1)
+ return (0);
+ return (1);
+}
+
+static void
+lint2()
+{
+ char *path, **args;
+
+ args = xcalloc(1, sizeof (char *));
+
+ path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint2"));
+ (void)sprintf(path, "%s/lint2", PATH_LIBEXEC);
+
+ appcstrg(&args, path);
+ applst(&args, l2flags);
+ applst(&args, l2libs);
+ applst(&args, p2in);
+
+ runchild(path, args, p2out);
+ free(path);
+ freelst(&args);
+ free(args);
+}
+
+static void
+cat(srcs, dest)
+ char *const *srcs;
+ const char *dest;
+{
+ int ifd, ofd, i;
+ char *src, *buf;
+ ssize_t rlen;
+
+ if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) {
+ warn("cannot open %s", dest);
+ terminate(-1);
+ }
+
+ buf = xmalloc(MBLKSIZ);
+
+ for (i = 0; (src = srcs[i]) != NULL; i++) {
+ if ((ifd = open(src, O_RDONLY)) == -1) {
+ free(buf);
+ warn("cannot open %s", src);
+ terminate(-1);
+ }
+ do {
+ if ((rlen = read(ifd, buf, MBLKSIZ)) == -1) {
+ free(buf);
+ warn("read error on %s", src);
+ terminate(-1);
+ }
+ if (write(ofd, buf, (size_t)rlen) == -1) {
+ free(buf);
+ warn("write error on %s", dest);
+ terminate(-1);
+ }
+ } while (rlen == MBLKSIZ);
+ (void)close(ifd);
+ }
+ (void)close(ofd);
+ free(buf);
+}
+
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..4e4fec2
--- /dev/null
+++ b/usr.bin/yacc/Makefile
@@ -0,0 +1,15 @@
+# @(#)Makefile 5.3 (Berkeley) 5/12/90
+# $Id$
+
+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
+LINKS+= ${BINDIR}/yacc ${BINDIR}/byacc
+MLINKS+=yacc.1 byacc.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..8275106
--- /dev/null
+++ b/usr.bin/yacc/closure.c
@@ -0,0 +1,312 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char const sccsid[] = "@(#)closure.c 5.3 (Berkeley) 5/24/93";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include "defs.h"
+
+short *itemset;
+short *itemsetend;
+unsigned *ruleset;
+
+static void set_EFF __P((void));
+#ifdef DEBUG
+static void print_closure __P((int));
+static void print_EFF __P(());
+static void print_first_derives __P(());
+#endif
+
+static unsigned *first_derives;
+static unsigned *EFF;
+
+
+static void
+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
+}
+
+
+void
+set_first_derives()
+{
+ register unsigned *rrow;
+ register unsigned *vrow;
+ register int j;
+ register unsigned k;
+ register unsigned cword = 0;
+ 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);
+}
+
+
+void
+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
+}
+
+
+
+void
+finalize_closure()
+{
+ FREE(itemset);
+ FREE(ruleset);
+ FREE(first_derives + ntokens * WORDSIZE(nrules));
+}
+
+
+#ifdef DEBUG
+
+static void
+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);
+}
+
+
+static void
+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]);
+ }
+ }
+}
+
+
+static void
+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..237efe2
--- /dev/null
+++ b/usr.bin/yacc/defs.h
@@ -0,0 +1,362 @@
+/*
+ * 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
+ * $Id$
+ */
+
+#include <sys/cdefs.h> /* for __P macro */
+#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 */
+
+char *allocate __P((unsigned));
+void closure __P((short *, int));
+void create_symbol_table __P((void));
+void default_action_warning __P((void));
+void dollar_error __P((int, char *, char *));
+void dollar_warning __P((int, int));
+void done __P((int));
+void fatal __P((char *msg));
+void finalize_closure __P((void));
+void free_parser __P((void));
+void free_symbols __P((void));
+void free_symbol_table __P((void));
+void illegal_character __P((char *));
+void illegal_tag __P((int, char *, char *));
+void lalr __P((void));
+bucket *lookup __P((char *));
+void lr0 __P((void));
+bucket *make_bucket __P((char *));
+void make_parser __P((void));
+void no_grammar __P((void));
+void no_space __P((void));
+void open_error __P((char *));
+void output __P((void));
+void over_unionized __P((char *));
+void prec_redeclared __P((void));
+void reader __P((void));
+void reflexive_transitive_closure __P((unsigned *, int));
+void reprec_warning __P((char *));
+void restarted_warning __P((void));
+void retyped_warning __P((char *));
+void revalued_warning __P((char *));
+void set_first_derives __P((void));
+void syntax_error __P((int, char *, char *));
+void terminal_lhs __P((int));
+void terminal_start __P((char *));
+void tokenized_start __P((char *));
+void undefined_goal __P((char *));
+void undefined_symbol_warning __P((char *));
+void unexpected_EOF __P((void));
+void unknown_rhs __P((int));
+void unterminated_action __P((int, char *, char *));
+void unterminated_comment __P((int, char *, char *));
+void unterminated_string __P((int, char *, char *));
+void unterminated_text __P((int, char *, char *));
+void unterminated_union __P((int, char *, char *));
+void untyped_lhs __P((void));
+void untyped_rhs __P((int, char *));
+void used_reserved __P((char *));
+void verbose __P((void));
+void write_section __P((char **));
diff --git a/usr.bin/yacc/error.c b/usr.bin/yacc/error.c
new file mode 100644
index 0000000..3ccfb14
--- /dev/null
+++ b/usr.bin/yacc/error.c
@@ -0,0 +1,392 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char const sccsid[] = "@(#)error.c 5.3 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+/* routines for printing error messages */
+
+#include "defs.h"
+
+static void print_pos __P((char *, char *));
+
+void
+fatal(msg)
+char *msg;
+{
+ fprintf(stderr, "%s: f - %s\n", myname, msg);
+ done(2);
+}
+
+
+void
+no_space()
+{
+ fprintf(stderr, "%s: f - out of space\n", myname);
+ done(2);
+}
+
+
+void
+open_error(filename)
+char *filename;
+{
+ fprintf(stderr, "%s: f - cannot open \"%s\"\n", myname, filename);
+ done(2);
+}
+
+
+void
+unexpected_EOF()
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", unexpected end-of-file\n",
+ myname, lineno, input_file_name);
+ done(1);
+}
+
+
+static void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+restarted_warning()
+{
+ fprintf(stderr, "%s: w - line %d of \"%s\", the start symbol has been \
+redeclared\n", myname, lineno, input_file_name);
+}
+
+
+void
+no_grammar()
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", no grammar has been \
+specified\n", myname, lineno, input_file_name);
+ done(1);
+}
+
+
+void
+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);
+}
+
+
+void
+prec_redeclared()
+{
+ fprintf(stderr, "%s: w - line %d of \"%s\", conflicting %%prec \
+specifiers\n", myname, lineno, input_file_name);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+untyped_lhs()
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", $$ is untyped\n",
+ myname, lineno, input_file_name);
+ done(1);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+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);
+}
+
+
+void
+undefined_goal(s)
+char *s;
+{
+ fprintf(stderr, "%s: e - the start symbol %s is undefined\n", myname, s);
+ done(1);
+}
+
+
+void
+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..275f7d1
--- /dev/null
+++ b/usr.bin/yacc/lalr.c
@@ -0,0 +1,711 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char const sccsid[] = "@(#)lalr.c 5.3 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <stdlib.h>
+#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;
+
+static void add_lookback_edge __P((int, int, int));
+static void build_relations __P((void));
+static void compute_FOLLOWS __P((void));
+static void compute_lookaheads __P((void));
+static void digraph __P((short **));
+static void initialize_F __P((void));
+static void initialize_LA __P((void));
+static int map_goto __P((int, int));
+static void set_accessing_symbol __P((void));
+static void set_goto_map __P((void));
+static void set_maxrhs __P((void));
+static void set_reduction_table __P((void));
+static void set_shift_table __P((void));
+static void set_state_table __P((void));
+static short **transpose __P((short **, int));
+static void traverse __P((register int));
+
+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;
+
+
+void
+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();
+}
+
+
+
+static void
+set_state_table()
+{
+ register core *sp;
+
+ state_table = NEW2(nstates, core *);
+ for (sp = first_state; sp; sp = sp->next)
+ state_table[sp->number] = sp;
+}
+
+
+
+static void
+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;
+}
+
+
+
+static void
+set_shift_table()
+{
+ register shifts *sp;
+
+ shift_table = NEW2(nstates, shifts *);
+ for (sp = first_shift; sp; sp = sp->next)
+ shift_table[sp->number] = sp;
+}
+
+
+
+static void
+set_reduction_table()
+{
+ register reductions *rp;
+
+ reduction_table = NEW2(nstates, reductions *);
+ for (rp = first_reduction; rp; rp = rp->next)
+ reduction_table[rp->number] = rp;
+}
+
+
+
+static void
+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;
+}
+
+
+
+static void
+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++;
+ }
+ }
+ }
+}
+
+
+static void
+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. */
+
+static 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;
+ }
+}
+
+
+
+static void
+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);
+}
+
+
+
+static void
+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);
+}
+
+
+static void
+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;
+}
+
+
+
+static 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);
+}
+
+
+
+static void
+compute_FOLLOWS()
+{
+ digraph(includes);
+}
+
+
+static void
+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);
+}
+
+
+static void
+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);
+}
+
+
+
+static void
+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..96d87a9
--- /dev/null
+++ b/usr.bin/yacc/lr0.c
@@ -0,0 +1,673 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char const sccsid[] = "@(#)lr0.c 5.3 (Berkeley) 1/20/91";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include "defs.h"
+
+extern short *itemset;
+extern short *itemsetend;
+extern unsigned *ruleset;
+
+int nstates;
+core *first_state;
+shifts *first_shift;
+reductions *first_reduction;
+
+static void allocate_itemsets __P((void));
+static void allocate_storage __P((void));
+static void append_states __P((void));
+static void free_storage __P((void));
+static void generate_states __P((void));
+static int get_state __P((int));
+static void initialize_states __P((void));
+static void new_itemsets __P((void));
+static core *new_state __P((int));
+#ifdef DEBUG
+static void print_derives __P((void));
+#endif
+static void save_reductions __P((void));
+static void save_shifts __P((void));
+static void set_derives __P((void));
+static void set_nullable __P((void));
+
+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;
+
+
+static void
+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 *);
+}
+
+
+static void
+allocate_storage()
+{
+ allocate_itemsets();
+ shiftset = NEW2(nsyms, short);
+ redset = NEW2(nrules + 1, short);
+ state_set = NEW2(nitems, core *);
+}
+
+
+static void
+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);
+ }
+}
+
+
+static void
+free_storage()
+{
+ FREE(shift_symbol);
+ FREE(redset);
+ FREE(shiftset);
+ FREE(kernel_base);
+ FREE(kernel_end);
+ FREE(kernel_items);
+ FREE(state_set);
+}
+
+
+
+static void
+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();
+}
+
+
+
+static 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);
+}
+
+
+
+static void
+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;
+}
+
+
+static void
+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;
+}
+
+
+
+static 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);
+}
+
+
+#if 0
+/* 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]);
+ }
+}
+#endif
+
+
+static void
+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;
+ }
+}
+
+
+
+static void
+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;
+ }
+ }
+}
+
+
+static void
+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
+}
+
+#if 0
+free_derives()
+{
+ FREE(derives[start_symbol]);
+ FREE(derives);
+}
+#endif
+
+#ifdef DEBUG
+static void
+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
+
+
+static void
+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
+}
+
+
+#if 0
+free_nullable()
+{
+ FREE(nullable);
+}
+#endif
+
+
+void
+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..fb8fa71
--- /dev/null
+++ b/usr.bin/yacc/main.c
@@ -0,0 +1,488 @@
+/*
+ * 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.
+ *
+ * $Id: main.c,v 1.5 1997/01/12 21:29:47 steve Exp $
+ */
+
+#ifndef lint
+static char const copyright[] =
+"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char const sccsid[] = "@(#)main.c 5.5 (Berkeley) 5/24/93";
+#endif /* not lint */
+
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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;
+
+static void create_file_names __P((void));
+static void getargs __P((int, char **));
+static void onintr __P((int));
+static void open_files __P((void));
+static void set_signals __P((void));
+static void usage __P((void));
+
+
+void
+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);
+}
+
+
+static void
+onintr(signo)
+ int signo;
+{
+ done(1);
+}
+
+
+static void
+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
+}
+
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s [-dlrtv] [-b file_prefix] [-o output_file_name] [-p symbol_prefix] filename\n", myname);
+ exit(1);
+}
+
+
+static void
+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 'o':
+ if (*++s)
+ output_file_name = s;
+ else if (++i < argc)
+ output_file_name = argv[i];
+ else
+ usage();
+ continue;
+
+ 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);
+}
+
+
+static void
+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);
+
+ if (output_file_name != 0)
+ {
+ file_prefix = output_file_name;
+ len = strlen(file_prefix);
+ }
+ else
+ {
+ 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);
+ if (file_prefix == output_file_name)
+ {
+ /*
+ * XXX ".tab.c" here is OUTPUT_SUFFIX, but since its length is
+ * in various magic numbers, don't bother using the macro.
+ */
+ if (len >= 6 && strcmp(code_file_name + len - 6, ".tab.c") == 0)
+ strcpy(code_file_name + len - 6, CODE_SUFFIX);
+ else if (len >= 2 && strcmp(code_file_name + len - 2, ".c") == 0)
+ strcpy(code_file_name + len - 2, CODE_SUFFIX);
+ else
+ strcpy(code_file_name + len, CODE_SUFFIX);
+ }
+ else
+ 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);
+ if (file_prefix == output_file_name)
+ {
+#define BISON_DEFINES_SUFFIX ".h"
+ if (len >= 2 && strcmp(defines_file_name + len - 2, ".c") == 0)
+ strcpy(defines_file_name + len - 2, BISON_DEFINES_SUFFIX);
+ else
+ strcpy(defines_file_name + len, BISON_DEFINES_SUFFIX);
+ }
+ else
+ 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);
+ if (file_prefix == output_file_name)
+ {
+ if (len >= 6 && strcmp(verbose_file_name + len - 6, ".tab.c") == 0)
+ strcpy(verbose_file_name + len - 6, VERBOSE_SUFFIX);
+ else if (len >= 2 && strcmp(verbose_file_name + len - 2, ".c") == 0)
+ strcpy(verbose_file_name + len - 2, VERBOSE_SUFFIX);
+ else
+ strcpy(verbose_file_name + len, VERBOSE_SUFFIX);
+ }
+ else
+ strcpy(verbose_file_name + len, VERBOSE_SUFFIX);
+ }
+}
+
+
+static void
+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*/
+ return (0);
+}
diff --git a/usr.bin/yacc/mkpar.c b/usr.bin/yacc/mkpar.c
new file mode 100644
index 0000000..bb59d9d
--- /dev/null
+++ b/usr.bin/yacc/mkpar.c
@@ -0,0 +1,413 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char const sccsid[] = "@(#)mkpar.c 5.3 (Berkeley) 1/20/91";
+#endif /* not lint */
+
+#include <stdlib.h>
+#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;
+
+static action *add_reduce __P((action *, int, int));
+static action *add_reductions __P((int, action *));
+static void defreds __P((void));
+static void find_final_state __P((void));
+static void free_action_row __P((action *));
+static action *get_shifts __P((int));
+static action *parse_actions __P((int));
+static void remove_conflicts __P((void));
+static int sole_reduction __P((int));
+static void total_conflicts __P((void));
+static void unused_rules __P((void));
+
+
+void
+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();
+}
+
+
+static action *
+parse_actions(stateno)
+register int stateno;
+{
+ register action *actions;
+
+ actions = get_shifts(stateno);
+ actions = add_reductions(stateno, actions);
+ return (actions);
+}
+
+
+static 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);
+}
+
+static 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);
+}
+
+
+static 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);
+}
+
+
+static void
+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;
+ }
+}
+
+
+static void
+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);
+}
+
+
+static void
+remove_conflicts()
+{
+ register int i;
+ register int symbol;
+ register action *p, *pref = NULL;
+
+ 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;
+ }
+}
+
+
+static void
+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");
+}
+
+
+static 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);
+}
+
+
+static void
+defreds()
+{
+ register int i;
+
+ defred = NEW2(nstates, short);
+ for (i = 0; i < nstates; i++)
+ defred[i] = sole_reduction(i);
+}
+
+static void
+free_action_row(p)
+register action *p;
+{
+ register action *q;
+
+ while (p)
+ {
+ q = p->next;
+ FREE(p);
+ p = q;
+ }
+}
+
+void
+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..cc893bb
--- /dev/null
+++ b/usr.bin/yacc/output.c
@@ -0,0 +1,1303 @@
+/*
+ * 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.
+ *
+ * $Id: output.c,v 1.8 1997/04/28 03:36:12 steve Exp $
+ */
+
+#ifndef lint
+static char const sccsid[] = "@(#)output.c 5.7 (Berkeley) 5/24/93";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include "defs.h"
+
+static int default_goto __P((int));
+static void free_itemsets __P((void));
+static void free_reductions __P((void));
+static void free_shifts __P((void));
+static void goto_actions __P((void));
+static int is_C_identifier __P((char *));
+static int matching_vector __P((int));
+static void output_actions __P((void));
+static void output_base __P((void));
+static void output_check __P((void));
+static void output_debug __P((void));
+static void output_defines __P((void));
+static void output_prefix __P((void));
+static void output_rule_data __P((void));
+static void output_semantic_actions __P((void));
+static void output_stored_text __P((void));
+static void output_stype __P((void));
+static void output_table __P((void));
+static void output_trailing_text __P((void));
+static void output_yydefred __P((void));
+static void pack_table __P((void));
+static int pack_vector __P((int));
+static void save_column __P((int, int));
+static void sort_actions __P((void));
+static void token_actions __P((void));
+
+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;
+
+
+void
+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);
+}
+
+
+static void
+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);
+}
+
+
+static void
+output_rule_data()
+{
+ register int i;
+ register int j;
+
+
+ fprintf(output_file, "const 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, "const 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");
+}
+
+
+static void
+output_yydefred()
+{
+ register int i, j;
+
+ fprintf(output_file, "const 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");
+}
+
+
+static void
+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();
+}
+
+
+static void
+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);
+}
+
+static void
+goto_actions()
+{
+ register int i, j, k;
+
+ state_count = NEW2(nstates, short);
+
+ k = default_goto(start_symbol + 1);
+ fprintf(output_file, "const 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);
+}
+
+static 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);
+}
+
+
+
+static void
+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;
+}
+
+static void
+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++;
+ }
+ }
+}
+
+
+static void
+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. */
+
+static 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);
+}
+
+
+
+static 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);
+ }
+ }
+}
+
+
+
+static void
+output_base()
+{
+ register int i, j;
+
+ fprintf(output_file, "const 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};\nconst short %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};\nconst short %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);
+}
+
+
+
+static void
+output_table()
+{
+ register int i;
+ register int j;
+
+ ++outline;
+ fprintf(code_file, "#define YYTABLESIZE %d\n", high);
+ fprintf(output_file, "const 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);
+}
+
+
+
+static void
+output_check()
+{
+ register int i;
+ register int j;
+
+ fprintf(output_file, "const 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);
+}
+
+
+static 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);
+}
+
+
+static void
+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);
+ }
+}
+
+
+static void
+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);
+}
+
+
+static void
+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", tflag);
+ fprintf(code_file, "#elif YYDEBUG\n#include <stdio.h>\n#endif\n");
+ 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\n");
+ fprintf(output_file, "const char * const %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, "const char * const %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");
+}
+
+
+static void
+output_stype()
+{
+ if (!unionized && ntags == 0)
+ {
+ outline += 3;
+ fprintf(code_file, "#ifndef YYSTYPE\ntypedef int YYSTYPE;\n#endif\n");
+ }
+}
+
+
+static void
+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);
+}
+
+
+static void
+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);
+}
+
+
+static void
+free_itemsets()
+{
+ register core *cp, *next;
+
+ FREE(state_table);
+ for (cp = first_state; cp; cp = next)
+ {
+ next = cp->next;
+ FREE(cp);
+ }
+}
+
+
+static void
+free_shifts()
+{
+ register shifts *sp, *next;
+
+ FREE(shift_table);
+ for (sp = first_shift; sp; sp = next)
+ {
+ next = sp->next;
+ FREE(sp);
+ }
+}
+
+
+
+static void
+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..4566570
--- /dev/null
+++ b/usr.bin/yacc/reader.c
@@ -0,0 +1,1878 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char const sccsid[] = "@(#)reader.c 5.7 (Berkeley) 1/20/91";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+#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";
+
+static void add_symbol __P((void));
+static void advance_to_start __P((void));
+static void cachec __P((int));
+static void check_symbols __P((void));
+static void copy_action __P((void));
+static void copy_ident __P((void));
+static void copy_text __P((void));
+static void copy_union __P((void));
+static void declare_start __P((void));
+static void declare_tokens __P((int));
+static void declare_types __P((void));
+static char *dup_line __P((void));
+static void end_rule __P((void));
+static void expand_items __P((void));
+static void expand_rules __P((void));
+static void free_tags __P((void));
+static void get_line __P((void));
+static bucket *get_literal __P((void));
+static bucket *get_name __P((void));
+static int get_number __P((void));
+static char *get_tag __P((void));
+static int hexval __P((int));
+static void initialize_grammar __P((void));
+static void insert_empty_rule __P((void));
+static int is_reserved __P((char *));
+static int keyword __P((void));
+static int mark_symbol __P((void));
+static int nextc __P((void));
+static void pack_grammar __P((void));
+static void pack_names __P((void));
+static void pack_symbols __P((void));
+static void print_grammar __P((void));
+static void read_declarations __P((void));
+static void read_grammar __P((void));
+static void skip_comment __P((void));
+static void start_rule __P((bucket *, int));
+
+static void
+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;
+}
+
+
+static void
+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;
+ }
+ }
+}
+
+
+static 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);
+}
+
+
+static void
+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;
+ }
+}
+
+
+static 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);
+ }
+ }
+}
+
+
+static 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*/
+ return (0);
+}
+
+
+static void
+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;
+ }
+ }
+}
+
+
+static void
+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;
+ }
+}
+
+
+static void
+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;
+ }
+}
+
+
+static 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);
+}
+
+
+static 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);
+}
+
+
+static 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);
+}
+
+
+static 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));
+}
+
+
+static 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);
+}
+
+
+static 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);
+}
+
+
+static void
+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();
+ }
+ }
+}
+
+
+static void
+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;
+ }
+}
+
+
+static void
+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;
+}
+
+
+static void
+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;
+ }
+ }
+}
+
+
+static void
+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;
+}
+
+
+static void
+expand_items()
+{
+ maxitems += 300;
+ pitem = (bucket **) REALLOC(pitem, maxitems*sizeof(bucket *));
+ if (pitem == 0) no_space();
+}
+
+
+static void
+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();
+}
+
+
+static void
+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;
+}
+
+
+static void
+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;
+}
+
+
+static void
+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;
+}
+
+
+static void
+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;
+}
+
+
+static void
+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;
+}
+
+
+static void
+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;
+ }
+}
+
+
+static int
+mark_symbol()
+{
+ register int c;
+ register bucket *bp = NULL;
+
+ 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);
+}
+
+
+static void
+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();
+}
+
+
+static void
+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);
+}
+
+
+static void
+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;
+ }
+}
+
+
+static void
+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;
+ }
+ }
+}
+
+
+static void
+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);
+}
+
+
+static void
+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);
+}
+
+
+static void
+print_grammar()
+{
+ register int i, j, k;
+ int spacing = 0;
+ 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);
+ }
+}
+
+
+void
+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..c4f4a5a
--- /dev/null
+++ b/usr.bin/yacc/skeleton.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.
+ *
+ * $Id: skeleton.c,v 1.13 1997/04/28 03:36:13 steve Exp $
+ */
+
+#ifndef lint
+static char const sccsid[] = "@(#)skeleton.c 5.8 (Berkeley) 4/29/95";
+#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 const yysccsid[] = \"@(#)yaccpar 1.9 (Berkeley) 02/21/93\";",
+ "#endif",
+ "#include <stdlib.h>",
+ "#define YYBYACC 1",
+ "#define YYMAJOR 1",
+ "#define YYMINOR 9",
+ "#define YYLEX yylex()",
+ "#define YYEMPTY -1",
+ "#define yyclearin (yychar=(YYEMPTY))",
+ "#define yyerrok (yyerrflag=0)",
+ "#define YYRECOVERING (yyerrflag!=0)",
+ "#if defined(c_plusplus) || defined(__cplusplus)",
+ /* Declaring standard functions is too painful for C++. */
+ "#include <stdlib.h>",
+ "#else",
+ /* Declare standard functions to avoid depending on <stdlib.h>. */
+ "extern char *getenv();",
+ "extern void *realloc();",
+ "#endif",
+#if 0
+ "extern int yylex();",
+ "extern int yyparse();",
+#endif
+ "static int yygrowstack();",
+ 0
+};
+
+
+char *tables[] =
+{
+ "extern const short yylhs[];",
+ "extern const short yylen[];",
+ "extern const short yydefred[];",
+ "extern const short yydgoto[];",
+ "extern const short yysindex[];",
+ "extern const short yyrindex[];",
+ "extern const short yygindex[];",
+ "extern const short yytable[];",
+ "extern const 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 10000",
+ "#define YYMAXDEPTH 10000",
+ "#endif",
+ "#endif",
+ "#define YYINITSTACKSIZE 200",
+ "int yydebug;",
+ "int yynerrs;",
+ "int yyerrflag;",
+ "int yychar;",
+ "short *yyssp;",
+ "YYSTYPE *yyvsp;",
+ "YYSTYPE yyval;",
+ "YYSTYPE yylval;",
+ "short *yyss;",
+ "short *yysslim;",
+ "YYSTYPE *yyvs;",
+ "int yystacksize;",
+ 0
+};
+
+
+char *body[] =
+{
+ "/* allocate initial stack or double stack size, up to YYMAXDEPTH */",
+ "static int yygrowstack()",
+ "{",
+ " int newsize, i;",
+ " short *newss;",
+ " YYSTYPE *newvs;",
+ "",
+ " if ((newsize = yystacksize) == 0)",
+ " newsize = YYINITSTACKSIZE;",
+ " else if (newsize >= YYMAXDEPTH)",
+ " return -1;",
+ " else if ((newsize *= 2) > YYMAXDEPTH)",
+ " newsize = YYMAXDEPTH;",
+ " i = yyssp - yyss;",
+ " if ((newss = (short *)realloc(yyss, newsize * sizeof *newss)) == NULL)",
+ " return -1;",
+ " yyss = newss;",
+ " yyssp = newss + i;",
+ " if ((newvs = (YYSTYPE *)realloc(yyvs, newsize * sizeof *newvs)) == NULL)",
+ " return -1;",
+ " yyvs = newvs;",
+ " yyvsp = newvs + i;",
+ " yystacksize = newsize;",
+ " yysslim = yyss + newsize - 1;",
+ " return 0;",
+ "}",
+ "",
+ "#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 const char *yys;",
+ "",
+ " if ((yys = getenv(\"YYDEBUG\")))",
+ " {",
+ " yyn = *yys;",
+ " if (yyn >= '0' && yyn <= '9')",
+ " yydebug = yyn - '0';",
+ " }",
+ "#endif",
+ "",
+ " yynerrs = 0;",
+ " yyerrflag = 0;",
+ " yychar = (-1);",
+ "",
+ " if (yyss == NULL && yygrowstack()) goto yyoverflow;",
+ " 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 >= yysslim && yygrowstack())",
+ " {",
+ " 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;",
+ "#if defined(lint) || defined(__GNUC__)",
+ " goto yynewerror;",
+ "#endif",
+ "yynewerror:",
+ " yyerror(\"syntax error\");",
+ "#if defined(lint) || defined(__GNUC__)",
+ " 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 >= yysslim && yygrowstack())",
+ " {",
+ " 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 >= yysslim && yygrowstack())",
+ " {",
+ " goto yyoverflow;",
+ " }",
+ " *++yyssp = yystate;",
+ " *++yyvsp = yyval;",
+ " goto yyloop;",
+ "yyoverflow:",
+ " yyerror(\"yacc stack overflow\");",
+ "yyabort:",
+ " return (1);",
+ "yyaccept:",
+ " return (0);",
+ "}",
+ 0
+};
+
+
+void
+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..2f2ee2e
--- /dev/null
+++ b/usr.bin/yacc/symtab.c
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char const sccsid[] = "@(#)symtab.c 5.3 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+#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
+
+static int hash __P((char *));
+
+bucket **symbol_table;
+bucket *first_symbol;
+bucket *last_symbol;
+
+
+static 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);
+}
+
+
+void
+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;
+}
+
+
+void
+free_symbol_table()
+{
+ FREE(symbol_table);
+ symbol_table = 0;
+}
+
+
+void
+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..c2cfee5
--- /dev/null
+++ b/usr.bin/yacc/verbose.c
@@ -0,0 +1,391 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char const sccsid[] = "@(#)verbose.c 5.3 (Berkeley) 1/20/91";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include "defs.h"
+
+static void log_unused __P((void));
+static void log_conflicts __P((void));
+static void print_actions __P((int));
+static void print_conflicts __P((int));
+static void print_core __P((int));
+static void print_gotos __P((int));
+static void print_nulls __P((int));
+static void print_reductions __P((register action *, register int));
+static void print_shifts __P((register action *));
+static void print_state __P((int));
+
+static short *null_rules;
+
+void
+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);
+}
+
+
+static void
+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);
+ }
+ }
+}
+
+
+static void
+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");
+ }
+ }
+}
+
+
+static void
+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);
+}
+
+
+static void
+print_conflicts(state)
+int state;
+{
+ register int symbol, act = 0, number = 0;
+ 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]);
+ }
+ }
+ }
+ }
+}
+
+
+static void
+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);
+ }
+}
+
+
+static void
+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");
+}
+
+
+static void
+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);
+ }
+}
+
+
+static void
+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);
+ }
+ }
+}
+
+
+static void
+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);
+ }
+}
+
+
+static void
+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..680363c
--- /dev/null
+++ b/usr.bin/yacc/warshall.c
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char const sccsid[] = "@(#)warshall.c 5.4 (Berkeley) 5/24/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+static void transitive_closure __P((unsigned *, int));
+
+static void
+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;
+ }
+}
+
+void
+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..596d154
--- /dev/null
+++ b/usr.bin/yacc/yacc.1
@@ -0,0 +1,158 @@
+.\" 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
+.\" $Id: yacc.1,v 1.6 1997/02/22 19:58:04 peter Exp $
+.\"
+.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 ] [ -o
+.I output_filename
+.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-o \fIoutput_filename\fR
+The
+.B -o
+option causes
+.I yacc
+to write the generated code to
+.IR output_filename
+instead of the default file,
+.IR y.tab.c .
+.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..7aad08e
--- /dev/null
+++ b/usr.bin/yacc/yyfix.1
@@ -0,0 +1,114 @@
+.\" 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
+.\" $Id$
+.\"
+.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
+.Ar file
+.Op Ar tables
+.Sh DESCRIPTION
+Programs have historically used a script (often named
+.Dq :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
+is provided to simplify the transition.
+.Pp
+The first (and required) argument to
+.Nm
+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
+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 appeared 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..d04375c
--- /dev/null
+++ b/usr.bin/ypcat/Makefile
@@ -0,0 +1,6 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id$
+
+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..9b31994
--- /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 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$
+.\"
+.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 4
+.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..bdf13ed
--- /dev/null
+++ b/usr.bin/ypmatch/Makefile
@@ -0,0 +1,6 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id$
+
+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..4dfbebd
--- /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 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$
+.\"
+.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 4
+.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..ded9624
--- /dev/null
+++ b/usr.bin/ypmatch/ypmatch.c
@@ -0,0 +1,135 @@
+/*
+ * 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));
+ exit(1);
+ break;
+ }
+ }
+ exit(0);
+}
diff --git a/usr.bin/ypwhich/Makefile b/usr.bin/ypwhich/Makefile
new file mode 100644
index 0000000..e70cdb7
--- /dev/null
+++ b/usr.bin/ypwhich/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id$
+
+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..3f6db83
--- /dev/null
+++ b/usr.bin/ypwhich/ypwhich.c
@@ -0,0 +1,256 @@
+/*
+ * 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.h>
+struct dom_binding{};
+#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_resp_u.ypbind_error));
+ clnt_destroy(client);
+ return r;
+ }
+ }
+ clnt_destroy(client);
+
+ ss_addr = *(u_long *)ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_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->map, &master);
+ switch(r) {
+ case 0:
+ printf("%s %s\n", ypml->map, master);
+ free(master);
+ break;
+ default:
+ fprintf(stderr,
+ "YP: can't find the master of %s: Reason: %s\n",
+ ypml->map, yperr_string(r));
+ break;
+ }
+ y = 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